Hmbown/CodeWhale · warning · anyhow::Error
server '{server_name}' is not registered
Error message
server '{server_name}' is not registered What it means
McpRegistry::unregister_server() bails when there is no config entry for the given name — the server was never registered or was already unregistered. Note the asymmetry: stop_server() only removes the live client, so stop-then-unregister is a valid sequence; the error fires only when configs has no entry for the name.
Source
Thrown at crates/mcp/src/lib.rs:346
failed,
cancelled,
}
}
/// Stop a running server by removing its client.
pub fn stop_server(&mut self, server_name: &str) -> Result<()> {
self.clients
.remove(server_name)
.with_context(|| format!("server '{server_name}' is not running"))?;
Ok(())
}
/// Remove a server entirely (config and client).
pub fn unregister_server(&mut self, server_name: &str) -> Result<()> {
let had_config = self.configs.remove(server_name).is_some();
self.clients.remove(server_name);
if !had_config {
bail!("server '{server_name}' is not registered");
}
Ok(())
}
/// List all tools from all running servers, applying tool filters.
pub fn list_tools(&self) -> Result<Vec<McpToolDescriptor>> {
let mut out = Vec::new();
for (server_name, (_, filter)) in &self.configs {
let Some(client) = self.clients.get(server_name) else {
continue;
};
let tools = client.list_tools()?;
for tool in tools {
if !allowed_by_filter(&tool.tool_name, filter) {
continue;
}
let qualified_name = qualify_tool_name(server_name, &tool.tool_name);
out.push(McpToolDescriptor {View on GitHub (pinned to 0c42157ee5)
Solutions
- Confirm the exact registered name (the key used in the successful register_server call) before unregistering
- Make teardown idempotent by treating this specific bail as success when the goal is 'make sure it is gone'
- In setup code, only unregister servers you actually registered — track which registrations succeeded
Example fix
// before
registry.unregister_server(&server_name)?;
// after: idempotent teardown
if registry.unregister_server(&server_name).is_err() {
tracing::debug!("server '{server_name}' was not registered; nothing to remove");
} Defensive patterns
Strategy: validation
Validate before calling
// Only unregister names you successfully registered:
let mut registered: Vec<String> = Vec::new();
if registry.register_server(config.clone(), filter, client).is_ok() {
registered.push(config.name.clone());
}
// at teardown:
for name in registered { let _ = registry.unregister_server(&name); } Try / catch
match registry.unregister_server(&server_name) {
Ok(()) => Ok(()),
Err(err) if err.to_string().ends_with("is not registered") => Ok(()), // idempotent teardown
Err(err) => Err(err),
} Prevention
- Track successful registrations and tear down only those
- Copy server names from the same constant used at registration, never re-type them
- Treat 'not registered' as success in cleanup paths
When it happens
Trigger: Calling unregister_server("foo") when foo was never registered; calling it twice (double teardown in a shutdown path plus a drop handler); misspelling the server name; unregistering after a register_server call that itself failed earlier in the same setup routine.
Common situations: Error-handling paths that clean up partially-failed registrations; idempotent shutdown code that assumes unregister is a no-op for unknown names; names copied from config that drifted from the registered key.
Related errors
- MCP server '{}' collides with already-registered server '{ex
- MCP server '{name}' already exists in {}. Use `codewhale mcp
- Failed to call MCP method '{}': connection '{}' is not ready
- MCP connection '{}' was cancelled
- Failed to connect MCP server '{server_name}': server is disa
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/0cce34c6762b10bd.
Report an issue: GitHub.