# How to Handle Errors and Detect Corrupted Data When Using Kanzi: Complete Validation Guide

> Learn how Kanzi detects corrupted data through header integrity, block checksums, and output size verification. Handle errors using IOException or numeric codes.

- Repository: [flanglet/kanzi-cpp](https://github.com/flanglet/kanzi-cpp)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Kanzi detects corrupted data through three validation stages—header integrity, block checksums, and output size verification—raising `IOException` objects with specific error codes defined in [`src/Error.hpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/Error.hpp) or returning numeric codes through high-level APIs like `BlockDecompressor`.**

The `flanglet/kanzi-cpp` compression library treats every failure as a well-defined error condition. Whether you are streaming data through `CompressedInputStream` or using the convenient `BlockDecompressor` wrapper, understanding how to handle errors and detect corrupted data ensures your application can gracefully manage truncated files, bit-flips, or version mismatches.

## Three-Layer Validation Architecture

Kanzi validates bit-stream integrity at three distinct checkpoints. Each stage maps to a specific error code in [`src/Error.hpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/Error.hpp) and a corresponding location in the source code.

**Header validation** occurs in `CompressedInputStream::readHeader()`. The library verifies the magic word identifying the stream as `BITSTREAM_TYPE`, checks version compatibility, and validates a header-level checksum. Failures here raise `IOException` with `Error::ERR_INVALID_FILE` for magic word mismatches, `Error::ERR_STREAM_VERSION` for unsupported versions, or `Error::ERR_CRC_CHECK` for header corruption.

**Block checksum verification** happens inside `CompressedInputStream::run()` immediately after inverse transformation. If the stream was created with a hasher, Kanzi recomputes the **XXHash** (32- or 64-bit) of the decompressed block and compares it against the checksum stored in the block footer. A mismatch triggers `Error::ERR_CRC_CHECK`.

**Output size verification** is performed by `BlockDecompressor::run()` after the entire stream is consumed. The total bytes written are compared against the original size stored in the header. A discrepancy results in `Error::ERR_INVALID_FILE`.

## Error Codes and Exception Types

All error conditions are enumerated in **[`src/Error.hpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/Error.hpp)**. The library uses integer constants consistently across C++ and language bindings:

```cpp
// src/Error.hpp
enum ErrorCode {
    ERR_MISSING_PARAM      = 1,
    ERR_BLOCK_SIZE         = 2,
    ERR_INVALID_CODEC      = 3,
    // ...
    ERR_CRC_CHECK          = 19,
    ERR_UNKNOWN            = 127
};

```

The **`IOException`** class defined in [`src/io/IOException.hpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/io/IOException.hpp) extends `std::runtime_error` to couple human-readable messages with programmatic error codes:

```cpp
// src/io/IOException.hpp
class IOException : public std::runtime_error {
private:
    int _code;
public:
    IOException(const std::string& msg, int error)
        : std::runtime_error(msg + ". Error code: " + TOSTR(error)), _code(error) {}
    int error() const { return _code; }
};

```

Constructor validation in `CompressedInputStream` uses `std::invalid_argument` for illegal parameters such as invalid block sizes or checksum configurations, distinguishing programming errors from runtime corruption.

## Detecting Corruption in CompressedInputStream

When using the streaming API directly, error handling follows a lazy evaluation pattern. The constructor validates arguments immediately, but **header parsing** is deferred until the first `read()` call invokes `readHeader()`.

During block processing in `CompressedInputStream::run()`, each entropy-decoded block undergoes inverse transformation before its checksum is recomputed. This check is atomic per block, allowing early detection of localized corruption without processing the entire file.

## High-Level Error Handling with BlockDecompressor

The **`BlockDecompressor`** API in [`src/app/BlockDecompressor.cpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/app/BlockDecompressor.cpp) encapsulates the streaming logic and converts exceptions into numeric return codes. This approach is essential for C-style interfaces or when integrating with languages that prefer error codes over exceptions.

The decompressor catches `IOException` internally and returns the associated error code from [`Error.hpp`](https://github.com/flanglet/kanzi-cpp/blob/main/Error.hpp). Additionally, it performs the final output size verification mentioned above, ensuring the decompressed payload matches the header's recorded original size exactly.

## Practical Code Examples

### Exception-Based Error Handling with CompressedInputStream

Use this pattern when you prefer C++ exception handling and need granular control over streaming:

```cpp
#include "kanzi.hpp"
#include "io/CompressedInputStream.hpp"
#include "io/IOException.hpp"

int main() {
    try {
        std::ifstream ifs("sample.knz", std::ios::binary);
        if (!ifs) throw std::runtime_error("cannot open file");

        kanzi::CompressedInputStream cis(ifs, 0 /*default context*/);
        const std::size_t bufSize = 1<<20;          // 1 MiB buffer
        std::vector<kanzi::byte> buf(bufSize);

        while (!cis.eof()) {
            cis.read(reinterpret_cast<char*>(buf.data()), bufSize);
            std::size_t got = cis.gcount();
            // …process `got` bytes…
        }
    }
    catch (const kanzi::IOException& ex) {
        std::cerr << "Decompression failed: " << ex.what()
                  << " (code " << ex.error() << ")\n";
        // react to specific codes if needed
        if (ex.error() == kanzi::Error::ERR_CRC_CHECK) {
            std::cerr << "   → corrupted block detected!\n";
        }
    }
}

```

The constructor may throw `std::invalid_argument` for parameter errors, while `readHeader()` and block processing throw `IOException` for corruption.

### Return Code-Based Error Handling with BlockDecompressor

Use this pattern for robust applications that avoid exceptions or need to integrate with C-style error handling:

```cpp
#include "app/BlockDecompressor.hpp"

int main() {
    kanzi::Context ctx;
    ctx.putString("inputName",  "corrupted.knz");
    ctx.putString("outputName", "out.dat");
    ctx.putInt("verbosity", 2);

    kanzi::BlockDecompressor dec(ctx);
    uint64 totalRead = 0;
    int rc = dec.decompress(totalRead);

    if (rc != 0) {
        std::cerr << "Decompression error code: " << rc << "\n";
        // Translate using Error.hpp constants
        if (rc == kanzi::Error::ERR_CRC_CHECK) {
            std::cerr << "Data corruption detected\n";
        }
    } else {
        std::cout << "Successfully decompressed " << totalRead << " bytes.\n";
    }
}

```

### Python Wrapper Error Detection

The Python wrapper exposes the same integer error codes for language-agnostic handling:

```python
import kanzi_c_api as knz

def decompress(src, dst):
    rc, decoded = knz.decompress(src, dst)
    if rc != 0:
        print(f"Error {rc}: {knz.ErrorMessage(rc)}")
        if rc == knz.ERR_CRC_CHECK:
            print("   → corrupted data")
    else:
        print(f"Decompressed {decoded} bytes")

```

## Summary

- Kanzi validates compressed streams at **three stages**: header integrity, per-block XXHash checksums, and final output size verification.
- **Error codes** are centralized in [`src/Error.hpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/Error.hpp) and propagated via `IOException` objects or numeric return values from `BlockDecompressor`.
- **Header corruption** triggers `ERR_INVALID_FILE` or `ERR_STREAM_VERSION` during the lazy initialization of `CompressedInputStream`.
- **Data corruption** within blocks is detected via checksum mismatch in `CompressedInputStream::run()`, raising `ERR_CRC_CHECK`.
- Use `ex.error()` on caught `IOException` instances to programmatically distinguish between corruption, version mismatches, and parameter errors.

## Frequently Asked Questions

### What error code does Kanzi return for a corrupted file?

Kanzi returns `Error::ERR_CRC_CHECK` (integer value 19) when a block checksum fails validation during decompression. This indicates that the decompressed data does not match the XXHash stored in the block footer, signaling bit-rot or transmission errors.

### How can I distinguish between a version mismatch and actual data corruption?

Header version mismatches raise `Error::ERR_STREAM_VERSION` in `CompressedInputStream::readHeader()`, while data corruption inside valid blocks raises `Error::ERR_CRC_CHECK`. Both use `IOException`, but you can inspect `ex.error()` to differentiate: version errors occur before any block processing begins, whereas checksum errors occur during the streaming loop.

### Is it safe to continue reading a stream after catching an IOException?

No. Once `CompressedInputStream` throws an `IOException` due to failed header validation or checksum mismatch, the stream enters an error state that invalidates further operations. You must destroy the `CompressedInputStream` object and reopen the source to attempt recovery, preferably after verifying the input file's integrity externally.

### Where does Kanzi compute the checksums during compression?

The symmetric counterpart in [`src/io/CompressedOutputStream.cpp`](https://github.com/flanglet/kanzi-cpp/blob/main/src/io/CompressedOutputStream.cpp) computes XXHash checksums for each compressed block during the write process. These values are embedded in the block footer and later extracted by `CompressedInputStream::run()` for comparison, ensuring end-to-end integrity across the compression-decompression lifecycle.