headroomlabs-ai/headroom · error · ConfigError

invalid pipeline config TOML: {0}

Error message

invalid pipeline config TOML: {0}

What it means

The body parsed as JSON but the top-level value is not an object — e.g. a JSON array, string, or number (helpers.py:2519). The Anthropic/OpenAI-style request schema is a JSON object, so arrays like '[{...}]' or bare strings are rejected with the actual type name embedded in the message.

Source

Thrown at crates/headroom-core/src/transforms/pipeline/config.rs:221

pub struct ProseFieldConfig {
    pub min_bytes: usize,
    pub min_segments: usize,
    pub target_ratio: f64,
}

/// Knobs for the [`crate::transforms::pipeline::offloads::DiffNoise`]
/// offload. Lockfile suffixes are matched against the new-file path
/// at the end of each `diff --git` header.
#[derive(Debug, Clone, Deserialize)]
pub struct DiffNoiseConfig {
    pub min_lines: usize,
    pub lockfile_suffixes: Vec<String>,
    pub drop_whitespace_only_hunks: bool,
}

#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
    #[error("invalid pipeline config TOML: {0}")]
    Parse(#[from] toml::de::Error),
    #[error("could not read pipeline config file: {0}")]
    Io(std::io::Error),
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_default_str_does_not_panic() {
        // Embedded TOML must always deserialize cleanly.
        let _ = PipelineConfig::from_default_str();
    }

    #[test]
    fn defaults_match_documented_thresholds() {
        let cfg = PipelineConfig::default();

View on GitHub (pinned to 322425c43b)

Solutions

  1. Send a single JSON object at the top level: {"model": ..., "messages": [...]}
  2. Check for double serialization: if your payload is a str after dumps, you serialized twice
  3. Validate the body client-side: isinstance(payload, dict) before sending

Example fix

# before
requests.post(url, json=[{"model": "claude", "messages": msgs}])

# after
requests.post(url, json={"model": "claude", "messages": msgs})
Defensive patterns

Strategy: type-guard

Validate before calling

payload = json.loads(text)
if not isinstance(payload, dict):
    raise TypeError(f"expected a JSON object, got {type(payload).__name__}")

Type guard

def is_request_object(value: object) -> bool:
    return isinstance(value, dict)

Try / catch

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

Prevention

When it happens

Trigger: POSTing '[{"model": ...}]' (array-wrapped request) to a proxied route; sending a JSON string '"hello"' or a bare number; a client that wraps payloads for batching APIs and reuses the same code against headroom.

Common situations: Porting code from batch APIs that accept arrays; accidental double-serialization (json.dumps applied twice yields a string); test fixtures copied from other APIs.

Related errors


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