{"id":"1b9c3951621dc23d","repo":"encode/httpx","slug":"using-zstandarddecoder-make-sure-to-install","errorCode":null,"errorMessage":"Using 'ZStandardDecoder', ...Make sure to install httpx using `pip install httpx[zstd]`.","messagePattern":"Using 'ZStandardDecoder', \\.\\.\\.Make sure to install httpx using `pip install httpx\\[zstd\\]`\\.","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"httpx/_decoders.py","lineNumber":172,"sourceCode":"                # errors if a truncated or damaged data stream has been used.\n                self.decompressor.finish()  # pragma: no cover\n            return b\"\"\n        except brotli.error as exc:  # pragma: no cover\n            raise DecodingError(str(exc)) from exc\n\n\nclass ZStandardDecoder(ContentDecoder):\n    \"\"\"\n    Handle 'zstd' RFC 8878 decoding.\n\n    Requires `pip install zstandard`.\n    Can be installed as a dependency of httpx using `pip install httpx[zstd]`.\n    \"\"\"\n\n    # inspired by the ZstdDecoder implementation in urllib3\n    def __init__(self) -> None:\n        if zstandard is None:  # pragma: no cover\n            raise ImportError(\n                \"Using 'ZStandardDecoder', ...\"\n                \"Make sure to install httpx using `pip install httpx[zstd]`.\"\n            ) from None\n\n        self.decompressor = zstandard.ZstdDecompressor().decompressobj()\n        self.seen_data = False\n\n    def decode(self, data: bytes) -> bytes:\n        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:","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_decoders.py#L154-L190","documentation":"ImportError raised in ZStandardDecoder.__init__ when the zstandard package is not importable but a response arrives with 'Content-Encoding: zstd'. Like brotli, httpx pops 'zstd' from SUPPORTED_DECODERS at import time when the package is missing, so this is most often seen when manually constructing the decoder or when the package state is inconsistent. The fix is to install the zstd extra.","triggerScenarios":"Response with 'Content-Encoding: zstd' without zstandard installed (zstd is increasingly common via Cloudflare, Facebook, AWS); manually building ZStandardDecoder() in tests; lockfile missing the optional extra.","commonSituations":"Hitting endpoints fronted by Cloudflare (zstd enabled by default since 2023); fresh pip install httpx without extras; slim Docker images; requirements file that lists httpx but not the zstd extra.","solutions":["Install the extra: `pip install httpx[zstd]`.","Or directly: `pip install zstandard`.","Pin in pyproject: `httpx[zstd]>=0.27`.","Without installing, opt out: headers={'Accept-Encoding': 'gzip, deflate'} (no zstd)."],"exampleFix":"// before\npip install httpx\nresp = client.get(url)  # Cloudflare sends Content-Encoding: zstd\nresp.content  # ImportError\n// after\npip install 'httpx[zstd]'\n# or opt out:\nresp = client.get(url, headers={'Accept-Encoding': 'gzip, deflate'})","handlingStrategy":"validation","validationCode":"try:\n    import zstandard  # noqa: F401\n    HAS_ZSTD = True\nexcept ImportError:\n    HAS_ZSTD = False\n\nheaders = {'Accept-Encoding': 'gzip, deflate, zstd'} if HAS_ZSTD else {'Accept-Encoding': 'gzip, deflate'}\nresp = client.get(url, headers=headers)","typeGuard":"def supports_zstd() -> bool:\n    try:\n        import zstandard  # noqa: F401\n        return True\n    except ImportError:\n        return False","tryCatchPattern":"try:\n    body = resp.content\nexcept ImportError as exc:\n    if 'zstd' in str(exc).lower() or 'zstandard' in str(exc).lower():\n        import subprocess; subprocess.check_call(['pip', 'install', 'httpx[zstd]'])\n    else:\n        raise","preventionTips":["Install httpx with the zstd extra: pip install 'httpx[zstd]'.","Declare extras in pyproject/requirements.","Match Accept-Encoding to installed codecs (Cloudflare sends zstd by default).","Pin zstandard version to avoid wheel regressions."],"tags":["zstd","zstandard","dependencies","content-encoding"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}