You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
subagent: parent gets cancelled when an interrupt with resume=true continues a background subagent #54658
If you interrupt a background subagent with resume=true while a steer prompt waits in its inbox, the subagent continues. It runs the steer prompt, replies, and its session ends with idle succeeded.
The parent session never sees that reply. At interrupt time, the parent receives <subagent state="cancelled">Subagent cancelled</subagent>. No later notification arrives.
The parent should receive state="completed" with the final reply. If no steer prompt waits, the subagent stops, and cancelled is correct.
#50751 also loses a subagent reply, but the trigger is different. In #50751, a new prompt reaches the subagent after it finished. Here, an interrupt stops the subagent while it runs, and the subagent resumes.
Plugins
None. The bug reproduces without plugins.
OpenCode version
2.0.26
Steps to reproduce
In a parent session, call the subagent tool with background: true and a prompt that runs sleep 600.
While sleep runs, send the subagent a steer prompt: opencode api post /api/session/<child>/prompt -d '{"text":"Write STEERED to a file, then reply DONE.","delivery":"steer"}'
Interrupt the subagent with resume: opencode api post "/api/session/<child>/interrupt?resume=true"
The server returns {"interrupted":true}.
Read the parent messages. The last subagent notification has state="cancelled".
Read the subagent messages with opencode api session.message.list --param sessionID=<child>. The file exists, the last assistant message says DONE, and the session ends with idle succeeded.
Thanks for the detailed report. I was able to reproduce this on 2.0.26, and it still happens on the latest dev (707de58).
Steps, using a simulated model:
A parent session starts a background subagent whose shell call is held running.
A delivery: "steer" prompt is sent to the child.
interrupt?resume=true is called on the child and returns {"interrupted":true}.
Afterwards, the parent's only subagent notification is state="cancelled". Meanwhile the child handles the steer prompt, replies DONE and ends idle succeeded. No completed notification reaches the parent. The script below reproduces it.
The trigger is different from #50751, as you noted. The likely cause, not yet confirmed, is that the subagent's background job is settled as cancelled when the interrupt ends the current run, and the run that resumes for the steer prompt isn't linked back to the parent's notification.
Reproduction script (drive.ts)
// Repro for anomalyco/opencode#54658: interrupt?resume=true on a background subagent with a// pending steer prompt -> parent gets state="cancelled" although the child resumes and completes.import{Effect,Stream}from"effect"import{Llm,OpenCodeDriver}from"opencode-drive"consthas=(req: unknown,s: string)=>JSON.stringify(req).includes(s)exportdefaultOpenCodeDriver.use({
...(process.env.OC_DEV ? {opencode: {dev: process.env.OC_DEV}} : {}),tui: {recording: true},tools: ["shell"],},({ ui, llm, tools, opencode })=>Effect.gen(function*(){constshells=yield*tools.control("shell")yield*llm.serve((req)=>{if(has(req,"Write STEERED"))returnStream.make(Llm.text("DONE"))if(has(req,"<subagent"))returnStream.make(Llm.text("Noted."))if(has(req,"Launch a background subagent")&&has(req,"working in the background"))returnStream.make(Llm.text("Launched."))if(has(req,"Launch a background subagent"))returnStream.make(Llm.toolCall({index: 0,id: "call_sub",name: "subagent",input: {agent: "general",description: "sleep task",prompt: "Run sleep 600",background: true},}),Llm.finish("tool-calls"),)if(has(req,"Run sleep 600"))returnStream.make(Llm.toolCall({index: 0,id: "call_sleep",name: "shell",input: {command: "sleep 600",description: "sleep"}}),Llm.finish("tool-calls"),)returnStream.make(Llm.text("ok"))})yield*ui.submit("Launch a background subagent")yield*ui.waitFor("Launched.")constsleep=yield*shells.take("call_sleep")yield*sleep.progress("sleeping...\n")constall=yield*opencode.session.list()constitems: any[]=(allasany).data??(allasany).items??(allasany)constchild=items.find((s: any)=>s.parentID)constparent=items.find((s: any)=>!s.parentID)yield*Effect.log(`parent=${parent.id} child=${child.id}`)yield*opencode.session.prompt({sessionID: child.id,text: "Write STEERED to a file, then reply DONE.",delivery: "steer"}asany)constr=yield*opencode.session.interrupt({sessionID: child.id,resume: true})yield*Effect.log(`interrupt -> ${JSON.stringify(r)}`)yield*Effect.sleep(8000)constpm=yield*opencode.message.list({sessionID: parent.id}asany)constcm=yield*opencode.message.list({sessionID: child.id}asany)constpj=JSON.stringify(pm)constcj=JSON.stringify(cm)conststates=[...pj.matchAll(/<subagent[^>]*state=\\?"(\w+)\\?"/g)].map((m)=>m[1])yield*Effect.log(`PARENT subagent notifications: ${JSON.stringify(states)}`)yield*Effect.log(`CHILD replied DONE: ${cj.includes('"DONE"')||cj.includes("DONE\"")}`)yield*Effect.log(`CHILD messages: ${cj.slice(0,700)}`)yield*ui.screenshot("parent-final")}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
opencode-agent commentedon Oct 12, 2026
Thanks for the detailed report. I was able to reproduce this on 2.0.26, and it still happens on the latest
dev(707de58).Steps, using a simulated model:
delivery: "steer"prompt is sent to the child.interrupt?resume=trueis called on the child and returns{"interrupted":true}.Afterwards, the parent's only subagent notification is
state="cancelled". Meanwhile the child handles the steer prompt, repliesDONEand endsidle succeeded. Nocompletednotification reaches the parent. The script below reproduces it.The trigger is different from #50751, as you noted. The likely cause, not yet confirmed, is that the subagent's background job is settled as cancelled when the interrupt ends the current run, and the run that resumes for the steer prompt isn't linked back to the parent's notification.
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.