{"record":{"id":"c48ccdd89f94e732","repo":"Hmbown/CodeWhale","slug":"mcp-config-path-cannot-be-empty-c48ccd","errorCode":null,"errorMessage":"MCP config path cannot be empty","messagePattern":"MCP config path cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":58,"sourceCode":"use crate::network_policy::{Decision, NetworkPolicyDecider, host_from_url};\nuse crate::utils::write_atomic;\n\n// === Error diagnostics helpers (#71) ===\n\n/// Bytes of a non-2xx response body to surface in connection errors.\nconst ERROR_BODY_PREVIEW_BYTES: usize = 200;\n\n/// Newest dated MCP protocol revision Codewhale advertises at `initialize` and\n/// answers as an MCP server. Matches the shared MCP crate (`crates/mcp`).\npub(crate) const MCP_PROTOCOL_VERSION: &str = \"2025-06-18\";\n/// Dated MCP revisions accepted during negotiation, newest first. A peer\n/// answering or requesting any of these continues the handshake.\npub(crate) const MCP_SUPPORTED_PROTOCOL_VERSIONS: &[&str] =\n    &[MCP_PROTOCOL_VERSION, \"2025-03-26\", \"2024-11-05\"];\n\nfn validate_mcp_config_path(path: &Path) -> Result<()> {\n    if path.as_os_str().is_empty() {\n        anyhow::bail!(\"MCP config path cannot be empty\");\n    }\n    if path\n        .components()\n        .any(|component| matches!(component, Component::ParentDir))\n    {\n        anyhow::bail!(\"MCP config path cannot contain '..' components\");\n    }\n    Ok(())\n}\n\n/// Expand `${NAME}` placeholders in an MCP config value from the process\n/// environment. This lets secrets (API keys, bearer tokens, …) be supplied\n/// through environment variables instead of being written in cleartext into\n/// the MCP config file on disk.\n///\n/// On a missing or malformed placeholder the error names only the offending\n/// variable, never the surrounding value, so a secret-bearing string is never\n/// echoed into logs or error output.","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L40-L76","documentation":"MCP config paths are validated before use; an empty path (empty OsStr) has no meaning and would break file operations downstream, so validate_mcp_config_path bails immediately. This guard runs on every MCP config load, mutation, mtime read, and workspace config-source switch.","triggerScenarios":"Passing an empty string as --mcp-config or equivalent config field; a config object where the mcp config path key is present but set to \"\"; programmatic calls to load_config / mutate_config / switch_workspace_config_source with an empty Path.","commonSituations":"Environment variable or config substitution evaluating to empty string; CLI flag given as `--mcp-config \"\"`; templated config where the path placeholder was never filled.","solutions":["Provide a non-empty path to your MCP config file (e.g. ~/.config/codewhale/mcp.json)","Remove the empty --mcp-config flag entirely to use defaults instead of passing \"\"","Fix the upstream substitution (env var, template) that produced the empty string"],"exampleFix":"# before\ncodewhale --mcp-config \"\" ...\n# after\ncodewhale --mcp-config ~/.config/codewhale/mcp.json ...","handlingStrategy":"validation","validationCode":"if (!mcpConfigPath || mcpConfigPath.trim() === \"\") {\n  throw new Error(\"MCP config path must be a non-empty path\");\n}","typeGuard":"const hasMcpPath = (p: string | undefined | null): p is string =>\n  typeof p === \"string\" && p.length > 0;","tryCatchPattern":"try {\n  loadMcpConfig(pathOpt.unwrap_or_default());\n} catch (e) {\n  if (String(e).includes(\"path cannot be empty\")) {\n    // fall back to default mcp config location or prompt user for a path\n  }\n}","preventionTips":["Validate CLI flags for empty strings before passing them through","Expand env/template placeholders and check the result is non-empty before assigning","Omit the flag entirely instead of passing \"\" to use defaults"],"tags":["mcp","path","validation"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}