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
- Read the per-thread reasons in the message; each names the thread id and the underlying route error (e.g. unknown provider, missing model).
- 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).
- Alternatively, let the affected threads finish or close them, then reload the config with no active routes to validate.
- 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
- Before removing or renaming providers/models in config, check which active threads route through them.
- Prefer additive config changes while sessions are running; do removals when no threads are active.
- Keep model aliases stable so stored thread identities keep resolving across config edits.
- Remember a rejected reload is atomic: the old config remains in force, so fix and retry rather than force-applying.
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
- context_window must be greater than 0
- custom provider '{provider_id}' must set [providers.{provide
- unknown field '{field_key}' for built-in provider '{provider
- unknown field '{field_key}' for custom provider '{provider_i
- invalid boolean '{raw}'
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/c0b7454d0449b71a.
Report an issue: GitHub.