Hmbown/CodeWhale · error
Failed to write MCP config {}: {}
Error message
Failed to write MCP config {}: {} What it means
Raised by save_mcp_config when utils::write_atomic fails to persist the serialized MCP config. write_atomic creates a temp file next to the target, writes, fsyncs, and renames, so the error covers temp-file creation, write, fsync, or rename failures (the parent directory is created first, so directory creation rarely fails). The message includes the target path and the underlying io::Error. Note write_atomic applies Private (0600-style) permissions, which some filesystems reject.
Source
Thrown at crates/tui/src/lib.rs:8886
}
McpServerDoctorStatus::Ok(format!(
"stdio server configured (command omitted; {} argument(s), {} environment binding(s))",
server.args.len(),
server.env.len()
))
}
fn save_mcp_config(path: &Path, cfg: &McpConfig) -> Result<()> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).with_context(|| {
format!("Failed to create MCP config directory {}", parent.display())
})?;
}
let rendered = serde_json::to_string_pretty(cfg)
.map_err(|e| anyhow!("Failed to serialize MCP config: {e}"))?;
crate::utils::write_atomic(path, rendered.as_bytes())
.map_err(|e| anyhow!("Failed to write MCP config {}: {}", path.display(), e))?;
Ok(())
}
fn run_sandbox_command(args: SandboxArgs) -> Result<()> {
use crate::sandbox::{CommandSpec, SandboxManager};
let SandboxCommand::Run {
policy,
network,
writable_root,
exclude_tmpdir,
exclude_slash_tmp,
cwd,
timeout_ms,
command,
} = args.command;
let policy = parse_sandbox_policy(View on GitHub (pinned to 8880682c63)
Solutions
- Check ownership and permissions of the config directory named in the message (ls -ld) and chown/chmod it for the current user
- Verify the target path is not an existing directory and remove stale temp siblings left by failed writes
- Free disk space and retry the command that saves the config
- If the directory cannot be made writable, move the config path to a writable location (set XDG_CONFIG_HOME or the equivalent override) or rerun as the owning user
Example fix
# before: config dir owned by root, write fails sudo codewhale mcp add my-server -- s npx -y server.js # after: fix ownership, run as the user sudo chown -R "$USER" ~/.config/codewhale codewhale mcp add my-server -- npx -y server.js
Defensive patterns
Strategy: validation
Validate before calling
// Rust: verify the destination is writable before triggering the save
fn mcp_config_writable(path: &std::path::Path) -> bool {
let parent = match path.parent() {
Some(p) if !p.as_os_str().is_empty() => p.to_path_buf(),
_ => std::path::PathBuf::from("."),
};
if std::fs::create_dir_all(&parent).is_err() || path.is_dir() {
return false;
}
let probe = parent.join(format!(".mcp-write-probe-{}", std::process::id()));
match std::fs::File::create(&probe) {
Ok(_) => { let _ = std::fs::remove_file(&probe); true }
Err(_) => false,
}
} Try / catch
// Branch on the underlying io error kind for recovery
if let Err(report) = save_mcp_config(&path, &cfg) {
let io = report.chain().find_map(|c| c.downcast_ref::<std::io::Error>());
match io.map(|e| e.kind()) {
Some(std::io::ErrorKind::PermissionDenied) => { /* fix dir ownership, retry once */ }
Some(std::io::ErrorKind::StorageFull) | Some(std::io::ErrorKind::WriteZero) => { /* free space, retry */ }
_ => return Err(report),
}
} Prevention
- Keep the MCP config directory owned by the invoking user
- Pre-flight check free space and write access before batch mcp operations
- Never point the MCP config path at a directory or a read-only mount
- Treat leftover temp siblings of the config file as evidence of a prior failed save
When it happens
Trigger: Saving MCP config to a path whose parent exists but is not writable (EACCES), a path that names an existing directory, a full filesystem during the temp write/fsync, a read-only mount, or a filesystem that cannot apply private permission bits (FAT/exFAT, some network mounts).
Common situations: Config directory owned by root or another user, XDG config dir on a read-only mount, disk exhausted during `mcp add`, path colliding with an existing directory, macOS TCC or Windows controlled-folder-access blocking the config dir.
Related errors
- MCP config path must be a regular file: {}
- CODEWHALE_SMOKE_ASSETS_DIR is not a directory: ${releaseAsse
- invalid JSON payload at key {MCP_SERVER_DEFINITIONS_KEY}; co
- invalid MCP server definition list in key {MCP_SERVER_DEFINI
- failed to parse permissions at {}; file contents were omitte
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/b377662aacb12e99.
Report an issue: GitHub.