Hmbown/CodeWhale · error

Config reload rejected because active thread routes are inva

Error message

Config reload rejected because active thread routes are invalid: {}

What it means

During a config reload, every active runtime thread's route (engine + provider identity + engine model) is re-resolved against the new config. If any thread would end up with an invalid route, the entire reload is rejected and the previous config stays in force; the message lists each failing thread id with its underlying route error. This keeps running turns from being stranded on a config they can no longer reconstruct.

Source

Thrown at crates/tui/src/runtime_threads.rs:2741

                    )
                })
                .collect()
        };

        let mut validated = Vec::with_capacity(entries.len());
        let mut failures = Vec::new();
        for (thread_id, engine, provider_identity, engine_model, active_turn_id) in entries {
            match resolve_runtime_thread_route_for_identity(
                &new_config,
                &provider_identity,
                Some(&engine_model),
            ) {
                Ok(route) => validated.push((thread_id, engine, route, active_turn_id)),
                Err(err) => failures.push(format!("{thread_id}: {err}")),
            }
        }
        if !failures.is_empty() {
            bail!(
                "Config reload rejected because active thread routes are invalid: {}",
                failures.join("; ")
            );
        }

        // `engine_load` is still held here, so a thread cannot construct an
        // engine from the accepted config before its process-wide read/tool
        // byte limits are active. Rejected reloads leave the prior limits in
        // place.
        let workshop_activation = crate::tools::large_output_router::WorkshopConfig::install_active(
            new_config.workshop.as_ref(),
        );
        let workflow_table = new_config.workflow_config();
        {
            let mut guard = self.config.write();
            *guard = new_config;
        }
        crate::tools::workflow::set_session_workflow_config(&self.workspace, workflow_table);

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Read the per-thread reasons in the message; each names the thread id and the underlying route error (e.g. unknown provider, missing model).
  2. Fix the new config so every active thread's provider identity and engine model still resolve (restore the provider entry, keep the model alias, or add a compatible route).
  3. Alternatively, let the affected threads finish or close them, then reload the config with no active routes to validate.
  4. Retry the reload only after the named threads resolve; the running system keeps the prior config until then, so nothing is lost by waiting.

Example fix

# before
# config.toml: provider "openai" removed, but thread t-42 still routes through it
codewhale /config reload
# bails: Config reload rejected because active thread routes are invalid: t-42: ...

# after
# config.toml: keep or restore a resolvable entry for the active thread's identity
[providers.openai]
api_key = "..."

codewhale /config reload   # accepted; new limits installed under held engine_load
Defensive patterns

Strategy: validation

Validate before calling

// Rust: dry-run route resolution for active threads before applying a config reload
let mut conflicts = Vec::new();
for (thread_id, provider_identity, engine_model) in active_thread_identities() {
    if resolve_runtime_thread_route_for_identity(&new_config, &provider_identity, Some(&engine_model)).is_err() {
        conflicts.push(thread_id);
    }
}
anyhow::ensure!(
    conflicts.is_empty(),
    "reload would strand threads {conflicts:?}; fix providers/models or close these threads first"
);
reload_config(new_config)?;

Try / catch

// Rust: the failed reload is safe (old config stays active); parse the per-thread list to report
match reload_config(&new_config) {
    Ok(()) => Ok(()),
    Err(err) if err.to_string().contains("active thread routes are invalid") => {
        // prior config still in force; surface thread ids from err and guide the user
        Err(anyhow::anyhow!("reload aborted, previous config active. Resolve routes for: {err}"))
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: Triggering a config reload while active threads exist whose provider/model no longer resolves under the incoming config: a provider block removed, a model renamed or no longer available, an API key/route default changed such that resolve_runtime_thread_route_for_identity fails for that thread's provider_identity and engine_model.

Common situations: Editing providers or models in the config file (or via /config reload) while sessions are running; removing a provider that an old thread still pins; renaming a model that active threads reference; rotating route defaults that break a thread's stored identity.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/c0b7454d0449b71a. Report an issue: GitHub.