Hmbown/CodeWhale · error
Cannot resume agent on its saved provider
Error message
Cannot resume agent {agent_id} on its saved provider '{}': {error} What it means
Resume rebinds the child to the provider pinned in its saved spawn route via `bind_spawn_provider`. If that provider is unavailable or cannot be bound (e.g. no direct provider configured for it), resume is rejected with the saved provider id embedded in the message.
Solutions
- Restore or fix the provider named in the error: re-add it to config and ensure its credentials are present.
- Check the wrapped `{error}` text from `bind_spawn_provider` for the concrete cause (missing key, unknown provider id).
- If the provider is gone for good, abandon resume and re-spawn the child on the now-current provider.
- Verify `[subagents]`/model routing config still lists the saved provider id.
Example fix
// before (config)
providers: { anthropic: { api_key: "..." } } # saved provider "openai" missing
// after
providers: { anthropic: { api_key: "..." }, openai: { api_key: "..." } } Defensive patterns
Strategy: validation
Validate before calling
// before resume, verify the saved provider is configured
let ok = configured_providers().contains(&saved_route.provider_id)
&& provider_has_credentials(&saved_route.provider_id); Try / catch
match agents.resume(agent_id) {
Err(e) if e.to_string().contains("on its saved provider") => {
// provider missing/unconfigured: respawn on current provider
spawn_new_agent(task, current_provider())
}
other => other?,
} Prevention
- Keep all providers referenced by active agents configured with valid credentials
- Audit provider removals against in-flight/interrupted children
- Pin child providers only to providers expected to persist for the session lifetime
When it happens
Trigger: Calling agents/resume on an agent whose saved route `provider_id` no longer resolves — `bind_spawn_provider` returns an Err for the saved provider, converted here into this anyhow error.
Common situations: Provider was removed or renamed in config since the child spawned; API key for that provider is missing/expired; resume replayed on a machine or profile where only a foreign/direct provider is configured; same-provider children unaffected because the bind is a no-op.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- Cannot resume agent : sub-agent depth limit reached…
- Agent has no continuable checkpoint to resume from…
- Antigravity is a retired, non-runnable legacy provider…
- Antigravity is a retired, non-runnable legacy provider…
- Choose a model for the destination provider before…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/118b753b5a00ed46.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:6308
if runtime.spawn_depth > runtime.max_spawn_depth {
return Err(anyhow!(
"Cannot resume agent {agent_id}: sub-agent depth limit reached (current {}, max {})",
runtime.spawn_depth,
runtime.max_spawn_depth
));
}
runtime.context.workspace = workspace;
// Rebind the child's saved provider pin (#6046). The resumed runtime
// is derived from the caller, whose active provider can differ from
// the provider the child was spawned under; without this rebind the
// saved model is validated against the caller's provider and rejected
// as a foreign model for a direct provider. `bind_spawn_provider` is
// a no-op when the pin already matches, so same-provider children are
// unaffected. Borrow the receipt: it is moved into the spawn options
// below.
if let Some(saved_route) = child_route.as_ref() {
bind_spawn_provider(&mut runtime, &saved_route.provider_id).map_err(|error| {
anyhow!(
"Cannot resume agent {agent_id} on its saved provider '{}': {error}",
saved_route.provider_id
)
})?;
}
let saved_manifest = self
.worker_records
.get(&agent_id)
.and_then(|record| record.spec.launch_manifest.as_ref());
let options = SubAgentSpawnOptions {
name: None, // the old session name stays owned by the terminal record
model: Some(model),
model_route: None,
child_route,
nickname: None,
fork_context,
write_claim: claim.as_ref().map(|(claim, _)| claim.clone()),
isolated_worktree: claimView on GitHub (pinned to 73e0f67d83)