Repository navigation
Description
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-prepareContextcallscontext.selectpackages/core/src/session/execution.ts:99-103- caught and onlyEffect.logError("Failed to drain Session", cause); nothing surfacespackages/core/src/session/execution.ts:138-145- the terminal lifecycle is published asExecution.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
- Start a session on a named agent; let a turn complete.
- Rename/remove that agent in config and trigger a reload (or save a plugin that reloads).
- Prompt the session again (or let a watcher resume it).
- Drain fails with
Session.AgentNotFoundError; the session endsfailed, nosession.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
- 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. - Surface the drain cause through
to-session-error.ts:73as a real session error, not onlylogError. - Don't publish
Execution.Failedwhen no step started; the prior turn already settled.
Related
- Session permanently frozen: drain() failures are logged but never surfaced to the client (no session.error event) #49740 - drain failures logged but never surfaced (general catch path; this is the agent-resolution cause).
- plugin: session-hook plugin activation triggers failed reload and wipes provider registry #52618 - plugin reload churn; mentions
Session.AgentNotFoundErrorfor the configured default agent.
Activity
github-actions commentedon Oct 6, 2026
This issue might be a duplicate of an existing issue. Please check:
opencodejr commentedon Oct 27, 2026
Thanks for the detailed write-up and the log analysis, @nathwn12.
There seem to be two separate causes here:
agent.updatedstorm in the same second. That's Agent not found for an existing agent while plugins reload after a plugin file changes #54172: during a reload, config-defined agents briefly disappear, and a step that starts in that window fails withAgentNotFoundError. It's fixed ondevby fix(core): wait for plugin activation before each runner step #51133 (each runner step now waits for plugin activation) and should be in an upcoming release.session.execution.failedevent, which carries the error (the TUI shows it as "Session failed"). TheFailed to drain Sessionlog line is extra diagnostics on top of that (see Session permanently frozen: drain() failures are logged but never surfaced to the client (no session.error event) #49740). The earlier, completed turn isn't lost: its messages and tool results stay in the session, as yoursession.getwith 18 messages shows. After the failure, the session goes idle and can be prompted again.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:
AgentNotFoundErrorAgentNotFoundErrorwhen sessions drain at server start before custom agents loadsession.execution.failed)davdroman commentedon Oct 31, 2026
Another data point for the fallback. In the TUI the problem usually doesn't show, because before each submit the TUI calls
session.switchAgentwhen 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 hitAgentNotFoundError. That includes TUI plugins usingctx.client.session.promptorctx.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:
{ "agents": { "foo": { "mode": "primary" } } }in.opencode/opencode.jsonc.POST /api/sessionwith{ "agent": "foo", "location": { "directory": "<project>" } }.footobarand set"default_agent": "bar". After the reload,GET /api/agentlists onlybar.POST /api/session/<id>/promptwith{ "text": "hi" }. The message fails withAgent not found: "foo", and the session still storesagent: "foo".A new session on
barin the same project answers normally.Falling back to
default_agentwhen the saved agent no longer resolves, and saving it on the session, would give every client the result the TUI already produces by switching.Agent not foundfor prompts sent without switching agents after the session's agent is removed #60756 as a duplicate of this issue