{"record":{"id":"4a56b3369da46fd5","repo":"Hmbown/CodeWhale","slug":"mcp-config-path-must-be-a-regular-file","errorCode":null,"errorMessage":"MCP config path must be a regular file: {}","messagePattern":"MCP config path must be a regular file: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3571,"sourceCode":"        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());\n    }\n\n    let mut file = open_mcp_config_file(path)\n        .with_context(|| format!(\"Failed to read MCP config {}\", path.display()))?;\n    let mut contents = String::new();\n    file.read_to_string(&mut contents)\n        .with_context(|| format!(\"Failed to read MCP config {}\", path.display()))?;\n    Ok(Some(contents))\n}\n\n#[cfg(unix)]\nfn open_mcp_config_file(path: &Path) -> std::io::Result<fs::File> {\n    use std::os::unix::fs::OpenOptionsExt;\n\n    fs::OpenOptions::new()\n        .read(true)\n        .custom_flags(libc::O_NOFOLLOW)\n        .open(path)","sourceCodeStart":3553,"sourceCodeEnd":3589,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L3553-L3589","documentation":"read_mcp_config_file stats the config path with symlink_metadata and rejects anything that is not a plain regular file: symlinks (even to regular files), directories, FIFOs, and device nodes all fail. This blocks symlink-swap attacks against the file that holds server commands, env secrets, and reviewed-plugin settings.","triggerScenarios":"Pointing the MCP config path at a symlink (e.g. ~/.codewhale/mcp.json linking into a dotfiles repo); the path being a directory or a named pipe.","commonSituations":"Dotfiles managers that symlink configs into place; provisioning scripts that accidentally create the path as a directory; container setups that expose the config through a symlink.","solutions":["Replace the symlink with the real file (copy the target into place) or use a hardlink/bind-mount instead","If a dotfiles manager owns the file, switch it to copy/template mode rather than symlink","Inspect with ls -l and remove any non-regular file occupying the path"],"exampleFix":"# before\n~/.codewhale/mcp.json -> /home/me/dotfiles/mcp.json  (symlink)\n\n# after\nmv ~/.codewhale/mcp.json ~/.codewhale/mcp.json.bak\ncp /home/me/dotfiles/mcp.json ~/.codewhale/mcp.json","handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(&mcp_json_path)?;\nlet ft = meta.file_type();\nif ft.is_symlink() || !ft.is_file() {\n    anyhow::bail!(\"MCP config path must be a real file, not a symlink or special file\");\n}\ncodewhale_tui::mcp::load_config(&mcp_json_path)?;","typeGuard":"fn is_regular_config_file(path: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|m| !m.file_type().is_symlink() && m.file_type().is_file())\n        .unwrap_or(false)\n}","tryCatchPattern":"match load_config(&path) {\n    Err(e) if e.to_string().contains(\"must be a regular file\") => {\n        // resolve the symlink target and copy it into place, then retry\n    }\n    other => other,\n}","preventionTips":["Configure dotfiles managers to copy/template mcp.json instead of symlinking it","Never point the config path at shared/ephemeral files (FIFOs, sockets, directories)","Check the path with ls -l during environment setup scripts"],"tags":["mcp","config","filesystem","security"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}