{"record":{"id":"25b27e7adf57d23d","repo":"aaif-goose/goose","slug":"failed-to-parse","errorCode":null,"errorMessage":"Failed to parse {}: {}","messagePattern":"Failed to parse (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-providers/src/declarative.rs","lineNumber":290,"sourceCode":"    let mut config = deserialize_provider_config(json)?;\n    resolve_config(&mut config)?;\n    Ok(config)\n}\n\npub fn load_custom_providers(dir: &Path) -> Result<Vec<DeclarativeProviderConfig>> {\n    if !dir.exists() {\n        return Ok(Vec::new());\n    }\n\n    std::fs::read_dir(dir)?\n        .filter_map(|entry| {\n            let path = entry.ok()?.path();\n            (path.extension()? == \"json\").then_some(path)\n        })\n        .map(|path| {\n            let content = std::fs::read_to_string(&path)?;\n            deserialize_provider_config(&content)\n                .map_err(|e| anyhow::anyhow!(\"Failed to parse {}: {}\", path.display(), e))\n        })\n        .collect()\n}\n\npub fn from_json(\n    json: &str,\n    tls_config: Option<TlsConfig>,\n    key_resolver: impl KeyResolver,\n) -> Result<Box<dyn Provider>> {\n    let config = config_from_json(json)?;\n\n    match config.engine {\n        ProviderEngine::OpenAI => openai::from_declarative_config(config, tls_config, key_resolver)\n            .map(|provider| Box::new(provider.build()) as Box<dyn Provider>),\n        ProviderEngine::Ollama => ollama::from_declarative_config(config, tls_config, key_resolver)\n            .map(|provider| Box::new(provider.build()) as Box<dyn Provider>),\n        ProviderEngine::Anthropic => {\n            anthropic::from_declarative_config(config, tls_config, key_resolver)","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/declarative.rs#L272-L308","documentation":"goose scans the declarative-providers directory for *.json files and deserializes each into DeclarativeProviderConfig (a strict serde schema: required name/engine/display_name/base_url, typed models array, unknown setup fields rejected). When one file fails deserialization, the error embeds the full file path and the underlying serde message so you know exactly which JSON is broken and why.","triggerScenarios":"Any .json file in the scanned providers dir that: misses a required field ('engine', 'base_url', ...), has a wrong type (\"context_limit\": \"4096\" as string), an unknown field inside 'setup', invalid JSON syntax (trailing comma, unquoted key), or an engine value serde can't map.","commonSituations":"Hand-authored custom provider JSON with a typo'd or missing key; files edited with an editor that broke JSON syntax; copying a config snippet from docs for a different goose version whose schema changed; leaving a template/example file with placeholder values in the directory.","solutions":["Read the message: it names the file and the serde error (e.g. 'missing field `engine` at line 3') — open that exact file and fix the cited field","Validate the file against a known-good bundled provider JSON (same top-level keys: name, engine, display_name, base_url, models, env_vars...)","Run it through a JSON linter (jq . file.json) to catch syntax errors first","If the file isn't meant to be a provider, move it out of the scanned directory or rename the extension away from .json"],"exampleFix":"// before (broken file.json)\n{ \"name\": \"my-provider\", \"engine\": \"openai_compatible\", \"base_url\": \"https://api.x.com/v1\" } // missing display_name\n\n// after\n{ \"name\": \"my-provider\", \"engine\": \"openai_compatible\", \"display_name\": \"My Provider\", \"base_url\": \"https://api.x.com/v1\", \"models\": [] }","handlingStrategy":"try-catch","validationCode":"fn provider_json_valid(path: &std::path::Path) -> anyhow::Result<()> {\n    let text = std::fs::read_to_string(path)?;\n    serde_json::from_str::<serde_json::Value>(&text)?; // syntax\n    deserialize_provider_config(&text)?;                // schema\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"// Don't let one bad file kill the whole scan: collect per-file outcomes, skip\n// invalid files with a warning, and report them together:\nlet mut ok = Vec::new();\nfor path in json_files {\n    match try_load(&path) {\n        Ok(cfg) => ok.push(cfg),\n        Err(e) => eprintln!(\"warning: skipping {}: {e}\", path.display()),\n    }\n}","preventionTips":["Lint every custom provider JSON with deserialize_provider_config in CI before it lands in the providers dir","Keep example/template files out of the scanned directory (different extension or directory)","Pin provider JSON files in version control and review them like code — schema changes then surface in diffs"],"tags":["json-config","provider-setup","validation","configuration"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}