{"record":{"id":"5ca8bad90b1c6ebb","repo":"apache/incubator-seata","slug":"failed-to-decompress-zstd-data","errorCode":null,"errorMessage":"Failed to decompress zstd data","messagePattern":"Failed to decompress zstd data","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"compressor/seata-compressor-zstd/src/main/java/org/apache/seata/compressor/zstd/ZstdUtil.java","lineNumber":54,"sourceCode":"\n        return Zstd.compress(bytes);\n    }\n\n    public static byte[] decompress(byte[] bytes) {\n        if (bytes == null) {\n            throw new NullPointerException(\"bytes is null\");\n        }\n        try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);\n                ZstdInputStream zis = new ZstdInputStream(bais);\n                ByteArrayOutputStream baos = new ByteArrayOutputStream()) {\n            byte[] buffer = new byte[8192];\n            int len;\n            while ((len = zis.read(buffer)) > 0) {\n                baos.write(buffer, 0, len);\n            }\n            return baos.toByteArray();\n        } catch (IOException e) {\n            throw new IllegalArgumentException(\"Failed to decompress zstd data\", e);\n        }\n    }\n}\n","sourceCodeStart":36,"sourceCodeEnd":58,"githubUrl":"https://github.com/apache/incubator-seata/blob/e01f97c6db397165050caa6764020410c2c8199a/compressor/seata-compressor-zstd/src/main/java/org/apache/seata/compressor/zstd/ZstdUtil.java#L36-L58","documentation":"Wrapped IOException from ZstdUtil.decompress thrown as IllegalArgumentException('Failed to decompress zstd data', e). ZstdInputStream fails when the payload lacks the zstd frame magic (0x28 0xB5 0x2F 0xFD), is truncated, or fails checksum validation. Note this class throws IllegalArgumentException, not RuntimeException, unlike the gzip/zip utils.","triggerScenarios":"Feeding non-zstd bytes (gzip/zip/lz4 output) to ZstdUtil.decompress; truncated arrays; corrupted stored payloads; passing an empty non-null array which makes ZstdInputStream throw on the first read.","commonSituations":"Compressor type mismatch between seata client and server; damaged binary columns in undo/branch tables; proxies altering bodies; upgrading seata where the default compressor changed to zstd while one side still sends uncompressed or differently-compressed data.","solutions":["Inspect the cause for zstd frame errors ('Unknown frame descriptor' = wrong format, short reads = truncation).","Align compressor type on all seata participants.","Regenerate corrupted persisted data; do not retry the same bytes.","Preflight check for the zstd magic bytes before decompressing untrusted payloads."],"exampleFix":"// before\nbyte[] out = ZstdUtil.decompress(bytes);\n\n// after\nif (bytes.length < 4 || (bytes[0] & 0xff) != 0x28 || (bytes[1] & 0xff) != 0xB5) {\n    throw new IllegalArgumentException(\"payload is not zstd framed\");\n}\nbyte[] out = ZstdUtil.decompress(bytes);","handlingStrategy":"try-catch","validationCode":"boolean isZstd(byte[] b) {\n    return b != null && b.length >= 4\n        && (b[0] & 0xff) == 0x28 && (b[1] & 0xff) == 0xB5\n        && (b[2] & 0xff) == 0x2F && (b[3] & 0xff) == 0xFD;\n}","typeGuard":null,"tryCatchPattern":"try {\n    byte[] out = ZstdUtil.decompress(bytes);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Failed to decompress zstd data\")) {\n        // wrong codec or truncated zstd frame; inspect e.getCause(); reject, don't retry\n    } else { throw e; }\n}","preventionTips":["After upgrading seata versions, re-verify the default compressor on every participant","Check the zstd frame magic before decompressing externally stored blobs","Store compressed blobs in binary-safe (BLOB/BYTEA) columns only"],"tags":["zstd","compression","corrupt-data","io","seata"],"backgroundTag":null,"analyzedSha":"e01f97c6db397165050caa6764020410c2c8199a","analyzedAt":"2026-08-14T10:23:53.097Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}