{"record":{"id":"a4d4409df38017a0","repo":"headroomlabs-ai/headroom","slug":"simulator-config-is-not-valid-json-0","errorCode":null,"errorMessage":"simulator config is not valid JSON: {0}","messagePattern":"simulator config is not valid JSON: (.+?)","errorType":"console","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"crates/headroom-simulators/src/config.rs","lineNumber":63,"sourceCode":"    pub sse: Vec<SseFrame>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct SseFrame {\n    #[serde(default)]\n    pub event: Option<String>,\n    pub data: Value,\n}\n\n#[derive(Debug, Error)]\npub enum ConfigError {\n    #[error(\"failed to read simulator config {path}: {source}\")]\n    Read {\n        path: String,\n        #[source]\n        source: std::io::Error,\n    },\n    #[error(\"simulator config is not valid JSON: {0}\")]\n    Parse(#[from] serde_json::Error),\n}\n\npub fn load_config(path: Option<&Path>) -> Result<SimulatorConfig, ConfigError> {\n    let Some(path) = path else {\n        return Ok(SimulatorConfig::default());\n    };\n    let raw = fs::read_to_string(path).map_err(|source| ConfigError::Read {\n        path: path.display().to_string(),\n        source,\n    })?;\n    Ok(serde_json::from_str(&raw)?)\n}\n\nfn default_status() -> u16 {\n    200\n}\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/crates/headroom-simulators/src/config.rs#L45-L81","documentation":"Raised by load_config when the file was read successfully but serde_json::from_str fails to parse it as a SimulatorConfig. The #[from] attribute converts any serde_json::Error (syntax error or type mismatch against the struct) into ConfigError::Parse. The embedded message pinpoints the line/column of the offending JSON.","triggerScenarios":"Passing a config file containing invalid JSON (trailing commas, comments, unquoted keys) or valid JSON that does not match SimulatorConfig's shape (e.g. 'sse' is not an array of {event?, data} objects).","commonSituations":"Hand-edited config with a trailing comma or JSONC comment, a YAML config fed where JSON is expected, or a schema drift after upgrading headroom-simulators (renamed/removed fields without #[serde(default)]).","solutions":["Validate the file with an external parser first: jq . sim.json — any syntax error is reported with a position.","Read the serde error message: 'expected struct SimulatorConfig' means a field type mismatch; fix the field named in the message.","Cross-check against the struct definition in crates/headroom-simulators/src/config.rs — note sse entries require a data field (event is optional via #[serde(default)]).","If you upgraded the crate recently, re-copy the example config from the new version and re-apply your changes."],"exampleFix":"// before\n{\"sse\": {\"event\": \"x\", \"data\": {}}}\n\n// after\n{\"sse\": [{\"event\": \"x\", \"data\": {\"role\": \"assistant\"}}]}","handlingStrategy":"validation","validationCode":"fn json_parses(raw: &str) -> Result<(), String> {\n    serde_json::from_str::<serde_json::Value>(raw)\n        .map(|_| ())\n        .map_err(|e| e.to_string())\n}\n\n// and schema check: the \"sse\" array entries need a \"data\" field\nlet v: serde_json::Value = serde_json::from_str(&raw)?;\nassert!(v.get(\"sse\").map_or(true, |s| s.as_array().is_some()));","typeGuard":null,"tryCatchPattern":"match serde_json::from_str::<SimulatorConfig>(&raw) {\n    Err(e) if e.classify() == serde_json::error::Category::Syntax => {\n        eprintln!(\"config is not valid JSON at line {}: fix syntax\", e.line());\n    }\n    Err(e) => eprintln!(\"config field mismatch: {e}\"),\n    Ok(cfg) => { /* proceed */ }\n}","preventionTips":["Lint config files in CI with jq so syntax errors never reach runtime.","Keep a checked-in example config that CI loads through load_config as a schema canary.","Avoid JSONC-style comments/trailing commas in configs consumed by serde_json."],"tags":["rust","config","json","serde"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}