{"record":{"id":"cd496bfb9aaba2b3","repo":"Hmbown/CodeWhale","slug":"mcp-server-server-method-failed-error","errorCode":null,"errorMessage":"MCP server '{server}': {method} failed: {error}","messagePattern":"MCP server '(.+?)': (.+?) failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":378,"sourceCode":"                Err(RecvTimeoutError::Disconnected) => {\n                    bail!(\n                        \"MCP server '{server}': process closed stdout before answering {method}{}\",\n                        self.exit_note()\n                    );\n                }\n            };\n\n            // Servers occasionally emit banners or log lines on stdout, and\n            // notifications carry no id. Both are skipped; only the matching\n            // response ends the wait.\n            let Ok(message) = serde_json::from_str::<Value>(&line) else {\n                continue;\n            };\n            if message.get(\"id\").and_then(Value::as_u64) != Some(id) {\n                continue;\n            }\n            if let Some(error) = message.get(\"error\") {\n                bail!(\"MCP server '{server}': {method} failed: {error}\");\n            }\n            return Ok(message.get(\"result\").cloned().unwrap_or(Value::Null));\n        }\n    }\n\n    /// The child's exit status, when it has one, for appending to a failure\n    /// message. Polls briefly because both callers run at the moment the child\n    /// is dying: the write can return EPIPE, or stdout can hit EOF, before the\n    /// kernel has finished reaping the process. Bounded and error-path-only,\n    /// so the cost buys a real diagnostic (\"exited with status 127\" is the\n    /// difference between a crashed server and a missing one).\n    fn exit_note(&mut self) -> String {\n        let deadline = Instant::now() + EXIT_STATUS_GRACE;\n        loop {\n            match self.child.try_wait() {\n                Ok(Some(status)) => return format!(\" (process exited with {status})\"),\n                Ok(None) if Instant::now() < deadline => {\n                    thread::sleep(Duration::from_millis(5));","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/mcp/src/stdio_client.rs#L360-L396","documentation":"The MCP server answered the request with a JSON-RPC error object, and the client surfaces it verbatim: 'MCP server '<name>': <method> failed: <error>'. This is a server-side rejection, not a transport failure — the protocol worked. The embedded error is the server's own message/code (e.g. 'Tool not found', 'Invalid params', 'Internal error').","triggerScenarios":"Calling a tool name the server does not implement; sending arguments that fail the server's schema validation; the tool's internal operation failing (auth expired on the server's side, upstream API error); the server rejecting the initialize params.","commonSituations":"Tool renamed or removed in a new server version while callers still use the old name; argument schemas drifted between client and server; expired credentials inside the server (GitHub token, DB password); rate limits or quota errors surfaced by the underlying API the tool wraps.","solutions":["Read the embedded error object — its code and message come from the server and name the actual problem","For unknown-tool errors, list current tools (list_tools) and use the exact advertised name","For invalid-params errors, match your arguments to the tool's inputSchema from the descriptor","For credential/quota errors, fix them on the server's configuration side, then retry"],"exampleFix":"// before\nlet v = registry.call_tool(\"github\", \"search_code\", json!({\"q\": \"repo:x y\"}))?;\n\n// after: inspect the server's error and correct the call\nmatch registry.call_tool(\"github\", \"search_code\", json!({\"q\": \"repo:x y\"})) {\n    Err(e) if e.to_string().contains(\"failed\") => {\n        let tools = registry.list_tools()?; // verify the tool name and its inputSchema\n        tracing::warn!(\"server rejected call: {e:#}\");\n    }\n    other => other?,\n}","handlingStrategy":"try-catch","validationCode":"// Verify the tool exists with the exact advertised name before calling:\nlet names: Vec<String> = registry.list_tools()?\n    .into_iter().map(|t| t.tool_name).collect();\nif !names.contains(&tool.to_string()) { bail!(\"unknown tool {tool}\"); }","typeGuard":null,"tryCatchPattern":"match registry.call_tool(server, tool, args) {\n    Ok(v) => Ok(v),\n    Err(err) if err.to_string().contains(\"failed:\") => {\n        // server-side JSON-RPC error: surface its message; fix args/name based on it\n        Err(err)\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Validate arguments against the tool's inputSchema before invoking","Pin server versions so tool names/schemas do not drift under you","Refresh credentials the server depends on before they expire"],"tags":["mcp","jsonrpc","server-error","tool-call"],"backgroundTag":"jsonrpc-server-error","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}