headroomlabs-ai/headroom · error · MagikaDetectorError

magika inference failed: {0}

Error message

magika inference failed: {0}

What it means

The request carries a Content-Encoding value other than the supported set (zstd/zstandard, gzip, deflate, br, identity), so headroom refuses to guess how to decode it (helpers.py:2438). Anything non-empty that is not 'identity' hits this branch, including compound values like 'gzip, br'.

Source

Thrown at crates/headroom-core/src/transforms/magika_detector.rs:317

    }
    out
}

/// Errors from the magika detector. Wraps the underlying `magika::Error`
/// so callers can match on whether init or inference broke without
/// pulling magika types into their imports.
#[derive(Debug, Error)]
pub enum MagikaDetectorError {
    /// One-time session initialization failed (model load, ONNX init).
    /// Once we hit this, every subsequent call also fails — there is no
    /// retry path here. The router should surface and stop.
    #[error("magika session init failed: {0}")]
    Init(String),

    /// Inference call failed for this input. Usually transient; future
    /// calls may succeed. The error message is the magika-side text;
    /// we don't try to wrap it.
    #[error("magika inference failed: {0}")]
    Inference(String),

    /// Singleton lock was poisoned (a previous holder panicked while
    /// holding it). The detector is unusable until the process
    /// restarts. We don't auto-recover — a panicked detector means
    /// something is corrupt and continuing would mask it.
    #[error("magika session lock poisoned")]
    Poisoned,
}

/// One-process singleton holding the magika session. Lazily
/// initialized on first call to [`magika_detect`].
///
/// `Mutex<Result<Session, ...>>` rather than `Result<Mutex<Session>>`
/// so init failure is recorded once and replayed cheaply on every
/// subsequent call (no re-attempting the load — if the model file is
/// missing or ort can't init, retrying just wastes cycles).
static MAGIKA_SESSION: OnceLock<Mutex<Result<Session, String>>> = OnceLock::new();

View on GitHub (pinned to 322425c43b)

Solutions

  1. Send a single supported encoding: identity, gzip, deflate, br, or zstd
  2. If you need layered compression, decompress on your side and send one encoding
  3. Omit Content-Encoding entirely for plain JSON bodies

Example fix

# before
headers = {"Content-Encoding": "gzip, br"}

# after
headers = {"Content-Encoding": "gzip"}
Defensive patterns

Strategy: validation

Validate before calling

SUPPORTED = {"", "identity", "gzip", "deflate", "br", "zstd", "zstandard"}
enc = request.headers.get("content-encoding", "").lower().strip()
if enc not in SUPPORTED:
    return JSONResponse({"error": f"Unsupported Content-Encoding: {enc}"}, status_code=400)

Try / catch

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

Prevention

When it happens

Trigger: Sending 'Content-Encoding: gzip, br', 'Content-Encoding: compress', a vendor-specific token, or a value with different casing/spacing that fails the exact string match (the code lowercases/strips, so 'GZIP' works but 'gzip;br' does not).

Common situations: Clients that apply layered compression and join encodings with commas; copy-pasted headers from responses into requests; future/new encodings like zstd variants before headroom supports them.

Related errors


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