{"record":{"id":"ee344c0bcdb5a1e9","repo":"Hmbown/CodeWhale","slug":"workspace-path-cannot-be-empty","errorCode":null,"errorMessage":"workspace path cannot be empty","messagePattern":"workspace path cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3833,"sourceCode":"    if !resolved.starts_with(plugin_path) {\n        anyhow::bail!(\"reviewed plugin MCP path escaped its staged root\");\n    }\n    Ok(resolved)\n}\n\nfn workspace_allows_project_mcp_config(workspace: &Path) -> bool {\n    crate::config::is_workspace_trusted(workspace)\n}\n\nfn checked_workspace_mcp_config_path(workspace: &Path) -> Result<PathBuf> {\n    Ok(checked_workspace_path(workspace)?\n        .join(\".codewhale\")\n        .join(\"mcp.json\"))\n}\n\nfn checked_workspace_path(workspace: &Path) -> Result<PathBuf> {\n    if workspace.as_os_str().is_empty() {\n        anyhow::bail!(\"workspace path cannot be empty\");\n    }\n    if workspace\n        .components()\n        .any(|component| matches!(component, Component::ParentDir))\n    {\n        anyhow::bail!(\"workspace path cannot contain '..' components\");\n    }\n    let absolute = if workspace.is_absolute() {\n        workspace.to_path_buf()\n    } else {\n        std::env::current_dir()\n            .context(\"failed to resolve current directory for workspace\")?\n            .join(workspace)\n    };\n    match absolute.canonicalize() {\n        Ok(path) => Ok(path),\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => {\n            Ok(normalize_path_components(&absolute))","sourceCodeStart":3815,"sourceCodeEnd":3851,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L3815-L3851","documentation":"checked_workspace_path is the entry guard for workspace-scoped MCP config lookups. An empty workspace string cannot identify a workspace, so it is rejected immediately, before any path is joined or the filesystem is touched.","triggerScenarios":"Passing an empty workspace (an unpopulated config field, an env var that expanded to nothing, or an empty string from argument parsing) into project MCP config loading or the checked_workspace_mcp_config_path helper.","commonSituations":"Calling workspace MCP helpers from scripts or tests with an uninitialized workspace variable; a launcher that starts the TUI before setting the workspace.","solutions":["Pass a non-empty workspace path (preferably absolute) from the caller","Default to std::env::current_dir() when the workspace is unknown","Fix the upstream config/CLI plumbing that produced the empty string"],"exampleFix":"// before\nlet path = checked_workspace_mcp_config_path(Path::new(&workspace_var))?;\n\n// after\nlet workspace = if workspace_var.is_empty() {\n    std::env::current_dir()?\n} else {\n    PathBuf::from(&workspace_var)\n};\nlet path = checked_workspace_mcp_config_path(&workspace)?;","handlingStrategy":"validation","validationCode":"// Reject empty workspaces at the boundary before any MCP config lookup:\nlet workspace = if workspace_input.as_os_str().is_empty() {\n    std::env::current_dir().context(\"no workspace supplied; using cwd\")?\n} else {\n    workspace_input.to_path_buf()\n};\nlet mcp_path = checked_workspace_mcp_config_path(&workspace)?;","typeGuard":"fn is_usable_workspace(p: &std::path::Path) -> bool {\n    !p.as_os_str().is_empty()\n        && !p.components().any(|c| matches!(c, std::path::Component::ParentDir))\n}","tryCatchPattern":null,"preventionTips":["Validate the workspace argument once at CLI/config parse time and fail with context","Default empty workspace inputs to the current directory instead of forwarding them","Log the resolved absolute workspace at startup to catch plumbing mistakes early"],"tags":["mcp","workspace","validation"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}