headroomlabs-ai/headroom · error · CcrBackendInitError

ccr redis backend init failed: {0}

Error message

ccr redis backend init failed: {0}

What it means

The 'zstandard' package IS installed, but decompressing the request body failed — the bytes are not a valid zstd stream. This is raised when ZstdDecompressor().stream_reader(raw).read() raises any non-ImportError exception (helpers.py:2411), e.g. zstandard.ZstdError for truncated or corrupted data.

Source

Thrown at crates/headroom-core/src/ccr/backends/mod.rs:72

    pub fn in_memory_default() -> Self {
        Self::InMemory {
            capacity: crate::ccr::DEFAULT_CAPACITY,
            ttl_seconds: crate::ccr::DEFAULT_TTL.as_secs(),
        }
    }
}

/// Reasons `from_config` may fail. Each variant is loud and recoverable
/// at the proxy startup boundary — the operator is told exactly what
/// went wrong rather than silently degrading to in-memory.
#[derive(Debug, Error)]
pub enum CcrBackendInitError {
    /// SQLite open / schema-create failed.
    #[error("ccr sqlite backend init failed: {0}")]
    Sqlite(#[from] rusqlite::Error),
    /// Redis open / PING failed (the smoke-test in `RedisCcrStore::open`).
    #[cfg(feature = "redis")]
    #[error("ccr redis backend init failed: {0}")]
    Redis(::redis::RedisError),
    /// Operator selected a backend whose feature flag was not compiled
    /// in. Loud failure rather than silent fallback.
    #[error(
        "ccr backend `{backend}` is not compiled in; rebuild with `--features {feature}` \
         or pick a different backend"
    )]
    UnsupportedBackend {
        backend: &'static str,
        feature: &'static str,
    },
}

#[cfg(feature = "redis")]
impl From<::redis::RedisError> for CcrBackendInitError {
    fn from(err: ::redis::RedisError) -> Self {
        Self::Redis(err)
    }

View on GitHub (pinned to 322425c43b)

Solutions

  1. Reproduce with the same body and verify it decompresses standalone: python -c "import zstandard,sys; zstandard.ZstdDecompressor().stream_reader(open('body.bin','rb').read()).read()"
  2. Fix or remove any intermediary that rewrites the body but keeps the Content-Encoding header
  3. In the client, compress exactly once and set Content-Encoding: zstd only when the payload is actually zstd-compressed

Example fix

# before: header says zstd but body is plain JSON
session.post(url, data=payload, headers={"Content-Encoding": "zstd"})

# after: compress the body or drop the header
session.post(url, data=zstandard.compress(payload), headers={"Content-Encoding": "zstd"})
Defensive patterns

Strategy: try-catch

Try / catch

try:
    body = await _read_request_body_bytes(request)
except ValueError as exc:
    if "zstd" in str(exc):
        return JSONResponse({"error": "invalid compressed body"}, status_code=400)

Prevention

When it happens

Trigger: Client sets 'Content-Encoding: zstd' but the body was only partially uploaded (Content-Length mismatch, connection cut mid-request), double-compressed, or is actually gzip/plain bytes. Also happens when a proxy in front of headroom already decompressed the body but left the header intact.

Common situations: An intermediate nginx/envoy decodes the body but forwards the original Content-Encoding header; a client bug that compresses twice; middleware that mutates the body without updating headers.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/ad87071d1d46f2a2. Report an issue: GitHub.