{"id":"d01a2e427ad7a309","repo":"encode/httpx","slug":"zstandard-data-is-incomplete","errorCode":null,"errorMessage":"Zstandard data is incomplete","messagePattern":"Zstandard data is incomplete","errorType":"exception","errorClass":"DecodingError","httpStatus":null,"severity":"error","filePath":"httpx/_decoders.py","lineNumber":199,"sourceCode":"        assert zstandard is not None\n        self.seen_data = True\n        output = io.BytesIO()\n        try:\n            output.write(self.decompressor.decompress(data))\n            while self.decompressor.eof and self.decompressor.unused_data:\n                unused_data = self.decompressor.unused_data\n                self.decompressor = zstandard.ZstdDecompressor().decompressobj()\n                output.write(self.decompressor.decompress(unused_data))\n        except zstandard.ZstdError as exc:\n            raise DecodingError(str(exc)) from exc\n        return output.getvalue()\n\n    def flush(self) -> bytes:\n        if not self.seen_data:\n            return b\"\"\n        ret = self.decompressor.flush()  # note: this is a no-op\n        if not self.decompressor.eof:\n            raise DecodingError(\"Zstandard data is incomplete\")  # pragma: no cover\n        return bytes(ret)\n\n\nclass MultiDecoder(ContentDecoder):\n    \"\"\"\n    Handle the case where multiple encodings have been applied.\n    \"\"\"\n\n    def __init__(self, children: typing.Sequence[ContentDecoder]) -> None:\n        \"\"\"\n        'children' should be a sequence of decoders in the order in which\n        each was applied.\n        \"\"\"\n        # Note that we reverse the order for decoding.\n        self.children = list(reversed(children))\n\n    def decode(self, data: bytes) -> bytes:\n        for child in self.children:","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_decoders.py#L181-L217","documentation":"DecodingError('Zstandard data is incomplete') raised in ZStandardDecoder.flush when, after all chunks were decoded, self.decompressor.eof is still False. This means the zstd stream did not reach its end-of-frame marker — the body was truncated before the final frame boundary. The flush() itself is a no-op for zstandard; this is purely an integrity check at stream close.","triggerScenarios":"Response body cut short before the zstd frame terminator; streaming endpoint that closes the connection mid-frame; proxy/load-balancer truncation; chunked-transfer body that ended one chunk too early.","commonSituations":"Connection drops mid-zstd-body; reverse proxy with a body size limit that cuts the stream; HTTP/2 stream reset before the final frame; server flushing a partial zstd buffer on timeout.","solutions":["Retry the request — truncation is often transient.","Increase client/server body-size limits and timeouts if the body legitimately exceeds them.","Opt out of zstd: headers={'Accept-Encoding': 'gzip, deflate'} to avoid the end-of-frame integrity check.","Inspect Content-Length vs num_bytes_downloaded to confirm and quantify truncation."],"exampleFix":"// before\nresp = client.get(url)  # zstd body truncated\nbody = resp.content  # DecodingError 'Zstandard data is incomplete'\n// after\nresp = client.get(url, headers={'Accept-Encoding': 'gzip, deflate'})\nbody = resp.content","handlingStrategy":"retry","validationCode":"# Before reading, sanity-check Content-Length if available\nexpected = resp.headers.get('Content-Length')\nif expected is not None and resp.num_bytes_downloaded > int(expected):\n    # likely truncation; avoid the flush() integrity check\n    resp.close()","typeGuard":null,"tryCatchPattern":"try:\n    body = resp.content\nexcept httpx.DecodingError as exc:\n    if 'incomplete' in str(exc).lower():\n        # truncated zstd; retry the request\n        resp = client.get(resp.request.url)\n        body = resp.content\n    else:\n        raise","preventionTips":["Retry truncated zstd responses — usually transient.","Compare Content-Length to num_bytes_downloaded to detect truncation early.","Keep a non-zstd Accept-Encoding fallback for flaky upstreams.","Tune client/server body-size and timeout limits."],"tags":["decoding","zstd","truncation","content-encoding"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}