{"record":{"id":"06d68b339ad1c8ae","repo":"headroomlabs-ai/headroom","slug":"failed-to-download-repo-from-huggingface-hub","errorCode":null,"errorMessage":"failed to download `{repo}` from HuggingFace Hub: {source}","messagePattern":"failed to download `(.+?)` from HuggingFace Hub: (.+?)","errorType":"exception","errorClass":"HfTokenizerError","httpStatus":null,"severity":"error","filePath":"crates/headroom-core/src/tokenizer/hf_impl.rs","lineNumber":46,"sourceCode":"\nuse thiserror::Error;\nuse tokenizers::Tokenizer as HfInner;\n\nuse super::{Backend, Tokenizer};\n\n#[derive(Debug, Error)]\npub enum HfTokenizerError {\n    /// The bytes / file did not parse as a valid HuggingFace `tokenizer.json`,\n    /// or the model component referenced an unsupported algorithm.\n    #[error(\"failed to load tokenizer for `{name}`: {source}\")]\n    Load {\n        name: String,\n        #[source]\n        source: Box<dyn std::error::Error + Send + Sync>,\n    },\n    /// The HuggingFace Hub fetch failed: network error, 404 on the repo, or\n    /// 401 on a gated model without an `HF_TOKEN`.\n    #[error(\"failed to download `{repo}` from HuggingFace Hub: {source}\")]\n    Hub {\n        repo: String,\n        #[source]\n        source: Box<dyn std::error::Error + Send + Sync>,\n    },\n}\n\n/// Token counter backed by a HuggingFace `tokenizer.json`.\n///\n/// Cheap to clone — internally an `Arc<tokenizers::Tokenizer>`. Construct once\n/// at startup, share across handlers.\n#[derive(Clone)]\npub struct HfTokenizer {\n    name: String,\n    inner: Arc<HfInner>,\n}\n\nimpl std::fmt::Debug for HfTokenizer {","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/crates/headroom-core/src/tokenizer/hf_impl.rs#L28-L64","documentation":"The request declared 'Content-Encoding: br' (Brotli) but the optional 'brotli' package is not installed (helpers.py:2432). Like zstd, brotli is a lazy optional dependency: the proxy only needs it when a brotli-encoded body actually arrives, so the error surfaces per-request rather than at startup.","triggerScenarios":"A client sends a body with 'Content-Encoding: br' (common from browsers and some Go/Rust HTTP clients that enable brotli by default) in an environment where 'pip install brotli' was never run.","commonSituations":"Slim production images without optional extras; a client library upgrade that turns on brotli request compression; testing from a browser-based tool that always sends br.","solutions":["pip install brotli (or add the Brotli extra to your deployment deps)","Disable brotli request compression in the client and use identity or gzip","Catch the ValueError and map it to a 400 with a hint so callers self-correct"],"exampleFix":"# before\nheaders = {\"Content-Encoding\": \"br\"}\nrequests.post(url, data=brotli.compress(payload), headers=headers)\n\n# after (package not installable): use identity\nrequests.post(url, data=payload)","handlingStrategy":"validation","validationCode":"try:\n    import brotli  # noqa: F401\n    can_brotli = True\nexcept ImportError:\n    can_brotli = False\nheaders = {\"Content-Encoding\": \"br\"} if can_brotli else {}","typeGuard":null,"tryCatchPattern":"try:\n    body = await _read_request_body_bytes(request)\nexcept ValueError as exc:\n    return JSONResponse({\"error\": {\"type\": \"invalid_request_error\", \"message\": str(exc)}}, status_code=400)","preventionTips":["Include brotli in the deployment extras if any browser-based client may hit the proxy","Disable brotli request compression in browser-facing fetch wrappers"],"tags":["compression","brotli","dependency","request-body"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}