Repository navigation
Closed as not planned
Closed as not planned
Copy link
Labels
Description
Verification
- I have checked that this feature has not been suggested before. Related existing issues are referenced below.
Problem
When a background subagent (spawned via the task tool) is interrupted mid-flight — by a service crash, OOM kill, network drop, or provider hang — the tool parts remain in running or pending state in the database. On resume via task_id, the orchestrator detects these orphaned parts but has no way to distinguish a genuine crash from a user-initiated cancel.
This causes two concrete issues:
- Stale
runningparts block resume: The resume path previously used.find()which only sanitized the first running tool part, leaving others orphaned. - No crash-vs-cancel distinction: Both user cancels and crashes produce the same
state=errorwith no metadata, making it impossible for the parent to decide whether to retry.
Current behavior (from source)
packages/opencode/src/tool/task.tsresume path: whentask_idis provided, the code fetches the last message and sanitizes tool parts. The original.find()only caught the first running part.packages/schema/src/v1/session.ts:ToolStateRunninghasstatus: "running",ToolStatePendinghasstatus: "pending"— both are incomplete states that should be resolved on resume.MessageV2.toModelMessagestreats bothrunningandpendingtool parts as incomplete, so orphaned parts silently break model context.
Proposed solution
In the resume path of task.ts, before re-prompting:
- Iterate ALL tool parts in the last message (not just the first) using
for...ofwithcontinue. - Sanitize both
runningandpendingparts tostate=errorwith:metadata.interrupted = true— distinguishes crash from user canceltime.end = Date.now()— ensuresend >= startinvariant- Preserve
metadatafrom running parts (e.g. markers, debug info)
- Exclude interrupted errors from terminal detection:
failedInLastshould filter outmetadata.interruptedparts so sanitized orphans don't cause false-positive failures.
Acceptance criteria
- After a service crash, resuming a background subagent via
task_idsanitizes all orphanedrunningandpendingtool parts toerrorwithmetadata.interrupted=true. - The parent session receives
state=errorwith arecovery_hintcontaining thetask_id, allowing retry. - User-initiated cancels do NOT set
metadata.interrupted(they use the existing cancel path). - All 28 unit tests in
test/tool/task.test.tspass, including new tests for pending sanitization and historical error handling.
Related issues
- [FEATURE]: Orchestrator agent for parallel subagent fan-out/fan-in (parallel dispatch without polling) #47639 — Orchestrator agent for parallel subagent fan-out/fan-in: the parent feature this fix supports. Background subagents need crash resilience to be useful in production.
- Hot reload MCP server configuration #37421 — Hot reload MCP server configuration: unrelated but referenced in [FEATURE]: apply plugin / MCP / config changes without restarting the running session #39987 for config resilience.
Implementation notes
- Single file change:
packages/opencode/src/tool/task.ts(~22 lines changed) - Test additions:
packages/opencode/test/tool/task.test.ts(3 new tests, 28 total) - Validated E2E in isolated lab: kill service mid-flight → restart → resume → tool parts sanitized correctly
- Quality gate: independent reviewer + tester passed with zero findings
Happy to open a PR if maintainers agree on the direction.
Activity
AllanSantos-DV commentedon Oct 24, 2026
Re-checked on current
dev(V2, c272a63). The path from this issue (tool/task.ts,MessageV2.toModelMessages) no longer exists, and the underlying problem is already handled:SessionRunner.settleStaleToolCalls(packages/core/src/session/runner/llm.ts) runs at the start of every drain and durably fails allstreaming/runningtool parts with anaborted"Tool execution interrupted" error (including the child sessionID forsubagent). Subagent recovery after a restart goes through the same path (session/execution/restart.ts), andsession-runner.test.tscovers it ("durably fails local tools left running by a prior process", "preserves a stale subagent child session ..."). So I think this can be closed as already fixed in V2. The only remaining difference is that V2 has no structured crash-vs-cancel flag (metadata.interrupted); I won't pursue that unless maintainers want it.opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up and for offering a PR!
This request targets the v1 codebase:
packages/opencode/src/tool/task.ts, thetask_idresume path andMessageV2.toModelMessages. V1 (0.x / 1.x, theopencode-aipackage) now only gets fixes for critical bugs, such as data loss, security problems or crashes with no workaround, so this improvement is unlikely to land there.OpenCode v2 (
@opencode/cli) is a separate implementation with its own subagent and restart handling. When a run is cut off, interrupted tool calls are marked as errors ("Tool execution interrupted: …", including the child session ID), and v2 has its own logic for sessions left behind by a process that died. We recommend upgrading to v2. These v2 issues are closely related, in case you want to follow them or add to them:cancelledwhen an interrupt withresume=truecontinues a background subagent #54658 / fix(core): keep subagent job across interrupt with resume #54660: background subagent continuing after an interrupt with resumeIf you see the same problem with background subagents on v2, or you think this is critical on v1, please reply here with details.
opencodejr commentedon Oct 29, 2026
Closing this as not planned. The request targets the v1 code (
packages/opencode/src/tool/task.ts, thetask_idresume path), and v1 only gets fixes for critical bugs, so this change is unlikely to land there.V2 is a separate implementation with its own handling for interrupted tool calls and restarts. If background subagents leave stale or misreported tool state on v2, please open a new issue with your version and steps to reproduce. You can also follow the related v2 work in #36349, #54658 and #59566. Thanks again for the detailed write-up!