headroomlabs-ai/headroom · error · MagikaDetectorError

magika session lock poisoned

Error message

magika session lock poisoned

What it means

After content-decoding, the body bytes could not be decoded as UTF-8 (helpers.py:2515, in the bytes-less JSON body reader). This means the decoded payload is binary or in another charset — JSON request bodies must be UTF-8 per RFC 8259. The message hints at compression because an undecoded compressed body is the most common cause: a client compressed the payload but sent no (or an 'identity') Content-Encoding header.

Source

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

#[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();

/// Default cap on magika ONNX session init.
///
/// On some platforms `Session::new()` can hang indefinitely instead of
/// returning an error. Root-caused on Windows: with `ort-load-dynamic`
/// (Windows-gated in `Cargo.toml`), the bare `LoadLibrary("onnxruntime.dll")`
/// search resolves to `C:\Windows\System32\onnxruntime.dll` — the Windows ML

View on GitHub (pinned to 322425c43b)

Solutions

  1. If the body is compressed, set the matching Content-Encoding header so the proxy decompresses it first
  2. Serialize JSON with ensure_ascii and UTF-8: json.dumps(obj).encode('utf-8')
  3. Send Content-Type: application/json; charset=utf-8 and verify the payload with a hex dump of the first bytes

Example fix

# before: compressed body, no header
requests.post(url, data=gzip.compress(json.dumps(payload).encode()))

# after
requests.post(url, data=gzip.compress(json.dumps(payload).encode()), headers={"Content-Encoding": "gzip"})
Defensive patterns

Strategy: try-catch

Validate before calling

raw.decode("utf-8")  # client-side pre-flight; if this fails, fix encoding or header

Try / catch

try:
    payload = await _read_json_body(request)
except ValueError as exc:
    return JSONResponse({"error": {"type": "invalid_request_error", "message": str(exc)}}, status_code=400)

Prevention

When it happens

Trigger: POSTing a gzip/zstd/brotli-compressed JSON body without a Content-Encoding header; sending JSON serialized as UTF-16 or Latin-1; binary garbage reaching the JSON route.

Common situations: Middleware that compresses bodies but strips headers; clients that pre-compress with no header; locale-sensitive serializers producing non-UTF-8 bytes.

Related errors


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