{"record":{"id":"8ef2cb682ca71da3","repo":"Hmbown/CodeWhale","slug":"error-runtime-api","errorCode":null,"errorMessage":"{error}","messagePattern":"\\{error\\}","errorType":"exception","errorClass":"McpManagementFailure","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_api.rs","lineNumber":4187,"sourceCode":"    Ok(value.to_owned())\n}\n\nasync fn mutate_mcp_management<T: Send + 'static>(\n    state: RuntimeApiState,\n    headers: axum::http::HeaderMap,\n    mutate: impl FnOnce(&RuntimeApiState, &mut crate::mcp::McpConfig) -> Result<T, ApiError>\n    + Send\n    + 'static,\n) -> Result<(T, String), ApiError> {\n    let expected = mcp_expected_revision(&headers)?;\n    #[cfg(test)]\n    let env_ticket = crate::test_support::env_scope_ticket();\n    tokio::task::spawn_blocking(move || {\n        #[cfg(test)]\n        let _membership = crate::test_support::join_env_scope(env_ticket);\n        let path = state.config.read().mcp_config_path();\n        crate::mcp::mutate_config(&path, Some(&expected), |config| {\n            mutate(&state, config).map_err(|error| anyhow::Error::new(McpManagementFailure(error)))\n        })\n        .map_err(mcp_mutation_error)\n    })\n    .await\n    .map_err(|_| ApiError::internal(\"MCP configuration write failed\"))?\n}\n\nasync fn mcp_management_snapshot(\n    state: RuntimeApiState,\n) -> Result<\n    (\n        (\n            crate::mcp::McpConfig,\n            std::collections::HashMap<String, &'static str>,\n        ),\n        String,\n    ),\n    ApiError,","sourceCodeStart":4169,"sourceCodeEnd":4205,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_api.rs#L4169-L4205","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: hand-edited config with duplicate server key\n{\"mcpServers\": {\"fs\": {...}, \"fs\": {...}}}\n// after: unique keys, valid JSON\n{\"mcpServers\": {\"fs\": {...}, \"git\": {...}}}","handlingStrategy":"try-catch","validationCode":"let raw = std::fs::read_to_string(state.config.read().mcp_config_path())?;\nserde_json::from_str::<codewhale_config::Config>(&raw).map_err(|e| format!(\"mcp config invalid: {e}\"))?;","typeGuard":null,"tryCatchPattern":"match api.mutate_mcp_config(...).await { Err(ApiError::Message(msg)) => eprintln!(\"mcp write failed: {msg}\"), Err(e) => eprintln!(\"task failure: {e}\"), Ok(v) => v }","preventionTips":["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"],"tags":["mcp","config","write-failed"],"backgroundTag":"file-write-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}