Hmbown/CodeWhale · warning · anyhow::Error

MCP pool has no configuration source to reload

Error message

MCP pool has no configuration source to reload

What it means

reload_from_config_sources(force=true) implements /mcp reload and startup reconnects: it stat's each recorded config source and re-reads when mtimes moved. If the pool has no recorded sources (config_sources empty — an ad-hoc pool built via McpPool::new or a default config with no files) and a forced reload is requested, it bails with this error instead of pretending to reload nothing. With force=false it just returns Ok(false).

Source

Thrown at crates/tui/src/mcp.rs:2522

    }

    /// If the source config file's mtime has changed since the last check,
    /// re-read it and (only when the content hash also changed) drop all
    /// existing connections so the next `get_or_connect` reattaches under
    /// the new config. No-op when the pool was constructed via [`McpPool::new`]
    /// (no source path), when stat fails, or when the file content is
    /// byte-identical to what we last loaded. Returns `Ok(true)` if any
    /// connections were dropped, `Ok(false)` otherwise.
    ///
    /// This is the lazy half of the auto-reload story for #1267: instead of a
    /// long-lived file watcher, the next tool invocation pays a single `stat`
    /// call (and only re-reads the file when the mtime moved). On networked
    /// or remote filesystems where mtime granularity is poor, the hash
    /// compare keeps us from churning connections on every check.
    fn reload_from_config_sources(&mut self, force: bool) -> Result<bool> {
        if self.config_sources.is_empty() {
            if force {
                anyhow::bail!("MCP pool has no configuration source to reload");
            }
            return Ok(false);
        }
        let current_mtimes: Vec<_> = self
            .config_sources
            .iter()
            .map(|path| mcp_config_mtime(path))
            .collect();
        if !force && current_mtimes == self.last_mtimes {
            return Ok(false);
        }
        // An mtime moved, or the user explicitly requested a reload: re-read
        // the complete global + workspace + plugin-backed config.
        let primary = self
            .config_sources
            .first()
            .context("MCP config source list unexpectedly empty")?;
        let new_config = if let Some(workspace) = self.workspace.as_deref() {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Construct the pool through from_config_path... / empty_with_workspace_config_sources so config_sources records the real file paths.
  2. If using a purely programmatic config, don't expose or invoke forced reload — replace the config via the switch/transactional APIs instead.
  3. Create the expected config file on disk so the reload has a source.
  4. Guard the UI: disable /mcp reload when no sources are recorded.

Example fix

// before
let mut pool = McpPool::new(McpConfig::default());
// later: /mcp reload -> 'MCP pool has no configuration source to reload'

// after
let mut pool = McpPool::from_config_path_with_workspace(&cfg_path, &workspace)?;
// reload now re-reads cfg_path and the workspace/trust candidates
Defensive patterns

Strategy: validation

Validate before calling

// Rust: only offer forced reload when sources exist
fn can_force_reload(pool: &McpPool) -> bool {
    !pool.config_sources().is_empty() // expose or inspect recorded sources
}
if can_force_reload(&pool) {
    pool.reload_from_config_sources(true)?;
} else {
    tracing::info!("no MCP config source recorded; nothing to reload");
}

Try / catch

// Rust: degrade gracefully when there is nothing to reload
match pool.reload_from_config_sources(true) {
    Err(e) if e.to_string().contains("no configuration source to reload") => {
        // informational: programmatic pool, skip the reload action
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling the forced-reload entry point (user runs /mcp reload, or a caller passes force=true) on a pool that was constructed directly from an McpConfig value rather than from a config file path, so no source paths were ever recorded.

Common situations: Embedding the pool with a programmatic config in tests or tools and then triggering the reload command; a session that started with a completely absent config file where even fallback sources resolved to nothing; callers assuming reload always has a file to re-read.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/57546bba273a2b00. Report an issue: GitHub.