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

  1. Restore or fix the provider named in the error: re-add it to config and ensure its credentials are present.
  2. Check the wrapped `{error}` text from `bind_spawn_provider` for the concrete cause (missing key, unknown provider id).
  3. If the provider is gone for good, abandon resume and re-spawn the child on the now-current provider.
  4. 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

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


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: claim

View on GitHub (pinned to 73e0f67d83)