Hmbown/CodeWhale · warning

MCP configuration changed; reload it before retrying this…

Error message

MCP configuration changed; reload it before retrying this server

What it means

The retry endpoint checks pool.config_matches(&config) after the enabled check. If the fresh config differs from the pool's snapshot, retry is refused and this error is recorded as the outcome, mirroring the connect endpoint's stale-pool guard but phrased for retries.

Solutions

  1. Reload the MCP configuration via the Runtime API reload endpoint (or restart the Runtime API), then retry the server.
  2. Diff the on-disk MCP config against the pool's snapshot to identify what changed unintentionally.
  3. Sequence automation: config edit -> reload -> retry, never config edit -> retry.

Example fix

// before
POST /mcp/retry {"server": "github"}  // -> stale config error

// after
POST /mcp/reload
POST /mcp/retry {"server": "github"}
Defensive patterns

Strategy: retry

Validate before calling

let pool_hash = fetch_pool_config_hash().await?;
if pool_hash != hash_of(&fetch_mcp_config().await?) {
    call_mcp_reload().await?;
}

Try / catch

match outcome.error {
    Some(e) if e.contains("configuration changed") => {
        client.reload_mcp_config().await?;
        client.mcp_retry(name).await?;
    }
    other => handle(other),
}

Prevention

When it happens

Trigger: Calling the MCP retry endpoint while the MCP configuration has been edited since the pool was loaded, so config_matches returns false; the per-server name in the request is enabled but the pool is stale.

Common situations: Adding a new server or changing env/args in the MCP config and immediately hitting retry without reload; CI or automation rewriting mcp config between calls.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/143da2c3c7f3c6da. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_api.rs:4683

/// return the actual result without replacing healthy sibling connections.
async fn reconnect_mcp_server(
    State(state): State<RuntimeApiState>,
    Path(name): Path<String>,
) -> Result<Json<McpServerActionReceipt>, ApiError> {
    let (config, _) = mcp_management_config(&state)?;
    if !config.servers.contains_key(&name) {
        return Err(ApiError::not_found(format!(
            "MCP server '{name}' not found"
        )));
    }
    let handle = mcp_pool_handle(&state, true)
        .await?
        .ok_or_else(|| ApiError::internal("MCP pool unavailable"))?;
    let mut pool = handle.lock().await;
    let error = if !config.servers[&name].is_enabled() {
        Some(anyhow::anyhow!("MCP server '{name}' is disabled"))
    } else if !pool.config_matches(&config) {
        Some(anyhow::anyhow!(
            "MCP configuration changed; reload it before retrying this server"
        ))
    } else {
        pool.retry_connection(&name).await.err()
    };
    let connection = mcp_connection_outcome(&pool, &name, error.as_ref());
    Ok(Json(McpServerActionReceipt {
        revision: None,
        name,
        action: if error.is_none() {
            "reconnected"
        } else {
            "reconnect_failed"
        },
        ok: error.is_none() && connection.connected,
        connection: Some(connection),
    }))
}

View on GitHub (pinned to 73e0f67d83)