Skip to content

Completed session marked failed, work reported lost, when its agent is missing at drain (Session.AgentNotFoundError) #53500

Open

Description

@nathwn12

Description

A session stores its agent id; SessionContext.select resolves it against the live registry on every drain. If the agent is absent - renamed/removed in config, or the registry mid-reload - the drain dies before any step runs:

  • packages/core/src/session/context.ts:129 - if (!agent.info) return yield* new AgentNotFoundError({ sessionID: session.id, agent: session.agent ?? agent.id })
  • packages/core/src/session/runner/llm.ts:198-199 - prepareContext calls context.select
  • packages/core/src/session/execution.ts:99-103 - caught and only Effect.logError("Failed to drain Session", cause); nothing surfaces
  • packages/core/src/session/execution.ts:138-145 - the terminal lifecycle is published as Execution.Failed

So a drain failure is a false death notice: the earlier turn already completed, yet the session is marked failed and a watcher reads the work as still open.

Evidence

~/.local/share/opencode/log/opencode.log, 2026-09-30..10-05: 22 message="Failed to drain Session" errors, 13 with cause="Session.AgentNotFoundError" (orchestrator x6, fleet x3, commander x2, vault x1, ops x1). Each is preceded in the same second by a plugin reload / watcher teardown / agent.updated storm, the named agent having just been renamed or removed. At 2026-10-01T19:27:27.926Z, GET /api/agent/orchestrator -> 404 precedes the drain failure for the same id.

Session ses_ef21d9befffe4cTcYTlZ4HYaWE (agent ops) ran 3 edit calls at 21:07:56-21:08:00Z plus a write at 21:08:27Z, all completed; its drain failed at 21:09:08.016Z with Agent not found: "ops". session.get shows 18 messages and outcome:"failed".

Steps to reproduce

  1. Start a session on a named agent; let a turn complete.
  2. Rename/remove that agent in config and trigger a reload (or save a plugin that reloads).
  3. Prompt the session again (or let a watcher resume it).
  4. Drain fails with Session.AgentNotFoundError; the session ends failed, no session.error.

Expected: the completed turn stays completed, and a missing agent surfaces as a recoverable session error (or the drain falls back to the default agent).
Actual: Failed to drain Session is logged and dropped; the session is marked failed.

OpenCode version

2.0.22 runtime (same failure in logs from 2.0.21). Source compared: v2.0.0-920-g54630586a7.

Operating System

Windows 11 Pro, build 26200 (AMD64)

Terminal

Windows Terminal (TERM=xterm-256color), PowerShell 7.6.6, bun 1.4.2

Suggested fix

  1. In context.ts:129, don't fail the drain when the stored agent is absent - fall back to the default agent or emit a durable recoverable session error.
  2. Surface the drain cause through to-session-error.ts:73 as a real session error, not only logError.
  3. Don't publish Execution.Failed when no step started; the prior turn already settled.

Related

Activity

  1. github-actions commented on Oct 6, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of an existing issue. Please check:

    • #49740: reports drain failures being logged without a corresponding user-visible session error; this issue details the missing-agent failure and its effect on the terminal session state.
  2. opencodejr commented on Oct 27, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up and the log analysis, @nathwn12.

    There seem to be two separate causes here:

    So what's left is a request to change that behaviour: when a session's stored agent no longer exists, fall back to the default agent, or report it as a recoverable error rather than marking the run failed. I've marked this as a feature request for maintainers to consider. This can't currently be done with a plugin, because plugins can't hook how a session's agent is resolved.

    Related:

  3. davdroman commented on Oct 31, 2026

    @davdroman

    Another data point for the fallback. In the TUI the problem usually doesn't show, because before each submit the TUI calls session.switchAgent when the selected agent differs from the session's saved one (packages/tui/src/component/prompt/index.tsx:1287). Clients that prompt without switching first still hit AgentNotFoundError. That includes TUI plugins using ctx.client.session.prompt or ctx.client.session.synthetic, which take no agent field. So the same session works when you type into it, but a plugin's continuation prompt fails with only a generic toast.

    Reproduced on 2.2.4:

    1. In a project, define { "agents": { "foo": { "mode": "primary" } } } in .opencode/opencode.jsonc.
    2. POST /api/session with { "agent": "foo", "location": { "directory": "<project>" } }.
    3. Rename foo to bar and set "default_agent": "bar". After the reload, GET /api/agent lists only bar.
    4. POST /api/session/<id>/prompt with { "text": "hi" }. The message fails with Agent not found: "foo", and the session still stores agent: "foo".

    A new session on bar in the same project answers normally.

    Falling back to default_agent when the saved agent no longer resolves, and saving it on the session, would give every client the result the TUI already produces by switching.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions