Hmbown/CodeWhale · error · McpManagementFailure
{error}
Error message
{error} What it means
This error carries the underlying MCP configuration mutation failure, wrapped in McpManagementFailure and surfaced through mcp_mutation_error. The write runs inside spawn_blocking under the config write lock; if the closure returns an error, its Display text becomes the message. The ApiError::internal('MCP configuration write failed') variant applies only when the blocking task itself panicked or was cancelled.
Solutions
- Read the embedded {error} text to identify the underlying cause and fix that first.
- Validate the MCP config file parses as the expected schema; fix or regenerate it.
- Check the mcp_config_path is writable by the process.
- If the task panicked, inspect the panic message; the generic 'MCP configuration write failed' means the closure never returned.
Example fix
// before: hand-edited config with duplicate server key
{"mcpServers": {"fs": {...}, "fs": {...}}}
// after: unique keys, valid JSON
{"mcpServers": {"fs": {...}, "git": {...}}} Defensive patterns
Strategy: try-catch
Validate before calling
let raw = std::fs::read_to_string(state.config.read().mcp_config_path())?;
serde_json::from_str::<codewhale_config::Config>(&raw).map_err(|e| format!("mcp config invalid: {e}"))?; Try / catch
match api.mutate_mcp_config(...).await { Err(ApiError::Message(msg)) => eprintln!("mcp write failed: {msg}"), Err(e) => eprintln!("task failure: {e}"), Ok(v) => v } Prevention
- Keep the MCP config valid JSON and schema-conformant; validate after hand edits
- Ensure the config path is writable by the app process
- Avoid concurrent mutations from multiple API clients; serialize changes
When it happens
Trigger: Calling the runtime API MCP config mutate endpoint (crates/tui/src/runtime_api.rs:4187) when crate::mcp::mutate_config fails: unreadable/corrupt mcp config file, serialization failure, or the mutate closure rejecting the change (e.g. duplicate server id).
Common situations: MCP config file hand-edited into invalid JSON; config path pointing somewhere unwritable; adding an MCP server that already exists; concurrent API calls racing on the same config.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Failed to parse MCP config
- Failed to parse MCP config
- Failed to parse MCP config; file contents were omitted
- Failed to read MCP config
- Failed to serialize MCP config
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8ef2cb682ca71da3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_api.rs:4187
Ok(value.to_owned())
}
async fn mutate_mcp_management<T: Send + 'static>(
state: RuntimeApiState,
headers: axum::http::HeaderMap,
mutate: impl FnOnce(&RuntimeApiState, &mut crate::mcp::McpConfig) -> Result<T, ApiError>
+ Send
+ 'static,
) -> Result<(T, String), ApiError> {
let expected = mcp_expected_revision(&headers)?;
#[cfg(test)]
let env_ticket = crate::test_support::env_scope_ticket();
tokio::task::spawn_blocking(move || {
#[cfg(test)]
let _membership = crate::test_support::join_env_scope(env_ticket);
let path = state.config.read().mcp_config_path();
crate::mcp::mutate_config(&path, Some(&expected), |config| {
mutate(&state, config).map_err(|error| anyhow::Error::new(McpManagementFailure(error)))
})
.map_err(mcp_mutation_error)
})
.await
.map_err(|_| ApiError::internal("MCP configuration write failed"))?
}
async fn mcp_management_snapshot(
state: RuntimeApiState,
) -> Result<
(
(
crate::mcp::McpConfig,
std::collections::HashMap<String, &'static str>,
),
String,
),
ApiError,View on GitHub (pinned to 73e0f67d83)