{"record":{"id":"ecaedb6f02a8adb9","repo":"Hmbown/CodeWhale","slug":"failed-to-parse-mcp-config-file-contents-were-ecaedb","errorCode":null,"errorMessage":"Failed to parse MCP config {}; file contents were omitted","messagePattern":"Failed to parse MCP config (.+?); file contents were omitted","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3553,"sourceCode":"    pub resources: Vec<McpDiscoveredItem>,\n    pub prompts: Vec<McpDiscoveredItem>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct McpManagerSnapshot {\n    pub config_path: std::path::PathBuf,\n    pub config_exists: bool,\n    pub reload_required: bool,\n    pub servers: Vec<McpServerSnapshot>,\n}\n\npub fn load_config(path: &Path) -> Result<McpConfig> {\n    validate_mcp_config_path(path)?;\n    let Some(contents) = read_mcp_config_file(path)? else {\n        return Ok(McpConfig::default());\n    };\n    serde_json::from_str(&contents).map_err(|_| {\n        anyhow::anyhow!(\n            \"Failed to parse MCP config {}; file contents were omitted\",\n            codewhale_config::quote_os_path(path)\n        )\n    })\n}\n\nfn read_mcp_config_file(path: &Path) -> Result<Option<String>> {\n    let metadata = match fs::symlink_metadata(path) {\n        Ok(metadata) => metadata,\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),\n        Err(err) => {\n            return Err(err)\n                .with_context(|| format!(\"Failed to inspect MCP config {}\", path.display()));\n        }\n    };\n    let file_type = metadata.file_type();\n    if file_type.is_symlink() || !file_type.is_file() {\n        anyhow::bail!(\"MCP config path must be a regular file: {}\", path.display());","sourceCodeStart":3535,"sourceCodeEnd":3571,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L3535-L3571","documentation":"load_config reads the MCP config file (user-level or workspace .codewhale/mcp.json) and deserializes it with serde into McpConfig. If the bytes are not valid JSON or do not match the McpServerConfig shape, parsing fails; the error deliberately omits file contents so secrets placed in env values are never echoed into logs.","triggerScenarios":"Calling load_config (directly or via add/remove/set_server_enabled, snapshot, or manager startup) on a file with a JSON syntax error, trailing comma, comment, or wrong field types (e.g. args as a string instead of an array of strings).","commonSituations":"Hand-editing mcp.json and leaving a trailing comma; pasting snippets that use JSON5 features; merge-conflict markers left in the file; an editor saving a BOM-prefixed file.","solutions":["Validate the file with a JSON linter (jq . mcp.json) and fix reported syntax errors","Check each server entry uses the expected types: command (string), args (array of strings), env (map string to string), enabled/disabled (bool)","Remove comments and trailing commas - the file must be strict JSON","Restore from a backup, or delete the file to fall back to McpConfig::default() and re-add servers"],"exampleFix":"// before (mcp.json)\n{ \"servers\": { \"a\": { \"command\": \"npx\", \"args\": [\"-y\", \"s\"], } } }\n\n// after\n{ \"servers\": { \"a\": { \"command\": \"npx\", \"args\": [\"-y\", \"s\"] } } }","handlingStrategy":"validation","validationCode":"// Dry-run the file through the same shape before codewhale loads it:\nlet contents = std::fs::read_to_string(&mcp_json_path)?;\nserde_json::from_str::<codewhale_tui::mcp::McpConfig>(&contents)\n    .context(\"mcp.json does not match the expected schema\")?;","typeGuard":"fn is_valid_mcp_config_json(raw: &str) -> bool {\n    serde_json::from_str::<codewhale_tui::mcp::McpConfig>(raw).is_ok()\n}","tryCatchPattern":"match load_config(&path) {\n    Err(e) if e.to_string().contains(\"Failed to parse MCP config\") => {\n        eprintln!(\"mcp.json is invalid JSON; run: jq . {}\", path.display());\n    }\n    other => other,\n}","preventionTips":["Run jq over mcp.json after every hand edit","Keep mcp.json under editor JSON validation (strict mode, no comments, no trailing commas)","Never store secrets in mcp.json env values that would discourage error output - the parse error intentionally hides contents"],"tags":["mcp","config","json","serde"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}