Hmbown/CodeWhale · error
MCP server ' ' has no command configured
Error message
MCP server '{}' has no command configured What it means
`spawn_with_timeouts` refuses to launch an MCP server subprocess whose configured `command` is empty or whitespace-only. There is no executable to spawn, so the client fails immediately with a clear config error instead of an obscure spawn failure deeper in `Command::new`. This is a configuration precondition check.
Solutions
- Set a valid, absolute path to the MCP server executable in the `command` field of the server's config.
- If the command comes from an environment variable, verify that variable is set in the environment the client runs in.
- If the server should run via a runtime (node/python), point command at the runtime binary and put the script in `args`.
- Validate config entries at load time so empty commands are rejected before they reach spawn.
Example fix
// before: config with empty command
{"name":"filesystem","command":"","args":["-y","@modelcontextprotocol/server-filesystem","/tmp"]}
// after: real executable
{"name":"filesystem","command":"npx","args":["-y","@modelcontextprotocol/server-filesystem","/tmp"]} Defensive patterns
Strategy: validation
Validate before calling
fn validate_mcp_config(cfg: &McpServerConfig) -> Result<(), String> {
if cfg.command.trim().is_empty() {
return Err(format!("server '{}' has empty command", cfg.name));
}
Ok(())
}
// call for every entry when configs load
for c in &configs { validate_mcp_config(c)?; } Try / catch
match spawn_with_timeouts(&config, hs, req).await {
Err(e) if e.to_string().contains("no command configured") => {
eprintln!("fill in the `command` field for server '{}' in your config", config.name);
}
other => other,
} Prevention
- Validate all MCP server configs at startup, before any spawn.
- Use absolute paths or well-known runtime binaries (npx, python3) for commands.
- If commands come from env vars, fail fast at config load when they resolve empty.
When it happens
Trigger: Calling `spawn_with_timeouts` (typically via client construction) with an `McpServerConfig` whose `command` field is `""`, contains only spaces, or was never set (defaulting to empty).
Common situations: A config file entry for the server missing the `command` key; an environment-variable interpolation that resolved to empty (e.g. `${MY_MCP_BIN}` unset); copy-pasting a server entry and forgetting to fill in the command; a tool generating configs that writes blank commands when discovery fails.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- MCP server ' ' has no command configured
- Failed to connect MCP server
- Failed to find MCP server
- failed to inherit reviewed plugin executable descriptor
- MCP pool has no configuration source to reload
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/274e41a73daad165.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mcp/src/stdio_client.rs:501
impl ChildProcessMcpClient {
/// Spawn `config.command` with `config.args`/`config.env` and complete the
/// MCP handshake.
///
/// Returns `Err` — never a degraded-but-usable client — when the command
/// cannot be executed, exits immediately, or does not answer `initialize`
/// within `HANDSHAKE_TIMEOUT`.
pub fn spawn(config: &McpServerConfig) -> Result<Self> {
Self::spawn_with_timeouts(config, HANDSHAKE_TIMEOUT, REQUEST_TIMEOUT)
}
fn spawn_with_timeouts(
config: &McpServerConfig,
handshake_timeout: Duration,
request_timeout: Duration,
) -> Result<Self> {
let server_name = config.name.clone();
if config.command.trim().is_empty() {
bail!("MCP server '{server_name}' has no command configured");
}
let mut command = Command::new(&config.command);
command
.args(&config.args)
.envs(&config.env)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
// The child's diagnostics belong on our stderr: stdout is the
// JSON-RPC channel and must not be polluted, and swallowing the
// child's stderr is how a misconfigured server becomes a silent
// one.
.stderr(Stdio::inherit());
// Own descendants too: shell/package launchers can exit before the
// actual server, leaving it alive with inherited protocol pipes.
#[cfg(unix)]
command.process_group(0);View on GitHub (pinned to 433685b202)