mirror of
https://github.com/open-webui/computer.git
synced 2026-11-05 01:17:24 +00:00
[GH-ISSUE #298] Critical bug: async subagent completions fork the chat tree onto stale branches, strand user turns on invisible side branches, and can leave a zombie turn that blocks all future completions #211
Labels
No labels
enhancement
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
open-webui/computer#211
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @Classic298 on GitHub (Sep 21, 2026).
Original GitHub issue: https://github.com/open-webui/computer/issues/298
Two related defects in how background subagent completions are attached to the chat. Both reproduced on 0.9.21 with the installed code, both confirmed against a real chat database after a live incident. Related to #296 (send-side fork) but a different code path: this is the injection side.
What happens
When a background subagent (delegate_task with background) finishes while one of your own turns is still running, its completion is queued as a pending internal message. The queue entry is attached to the last done assistant message at injection time. That message is an ancestor of your still-running turn, not its leaf. The entry is therefore born on a sibling branch that you never created.
When your turn later finishes or pauses, the pending queue is drained: the queued entry becomes a real user message plus a new assistant placeholder on that sibling branch, and the chat's current-message pointer is moved to it. Your own last turn is now on a dead side branch. The UI shows one path, the database contains a fork you did not create, and every follow-up message you send chains onto the fork because the frontend sends the chat's current pointer as the parent.
What I observed in a live chat
Three forks created by three subagent completions, each attached to the last done assistant while my own turn was in flight:
Net effect mid-conversation: replies arrive to messages you cannot see on your current path, your own turns look like they were never answered, and the tree keeps forking with every background completion that lands during a running turn.
The stranding detail
In the live incident one turn's tool call was denied by the auto-reviewer, so the turn paused in the approval-required state: done stays false and content stays empty. The drain fired 45ms later and repointed the chat to the fork, which took the approval prompt with it. The paused turn stayed paused forever. Because the injection path treats any not-done assistant as "chat is active", that zombie now makes every subsequent background completion queue as pending with no reply until some later drain, so one stranding poisons all future injections in that chat. That state is in my database right now: a turn from the incident is still not-done with a pending tool call from hours ago, and the most recent subagent completion is sitting unprocessed because of it.
Why it happens
The injection parent is computed as the last done assistant message. During a running turn that is an ancestor, not the leaf. The queue-drain then always moves the chat pointer to the injected branch, regardless of whether your branch's turn is still live or paused on approval. And the queue-vs-branch decision for completions uses the done flags of messages, never the server-side running-task state of the chat. It is the same root pattern as #296 from the other direction: #296 lets a user message become a fork because a stream died, this lets a system message become a fork because your turn is alive.
Reproduction
I reproduced this with the installed cptr modules against a scratch database: a chat with one done exchange, a second user turn whose assistant placeholder is not done (turn running), then a subagent completion injected while that turn runs. The completion lands as a sibling of the running turn's parent, with pending status:
asst1 now has two children. When turn 2 ends and the queue drains, the pending copy becomes a real internal message on the fork. Nobody created that fork: not regenerate, not an explicit branch, the injection path did it on its own.
The same happens with the turn paused on a denied approval instead of ended: the drain still runs and still repoints the chat pointer, so the approval UI for your branch disappears and the paused turn never resumes.
Consequences
Fix direction
Two independent corrections:
Additionally the startup reconcile pass only heals not-done turns with no running task. It should also detect approval-paused turns whose branch is no longer the chat's current path, mark them done or surface them, otherwise one incident permanently blocks background completions in that chat.
Workaround for now
None that fixes it. Avoid background subagents in tool-heavy chats where turns run long, and if a turn stalls on an approval, cancel it before sending anything. If a chat already has a stranding zombie, the not-done assistant row keeps poisoning completions until it is marked done manually in the database.
@owui-terminator[bot] commented on GitHub (Sep 21, 2026):
🔍 Related Issues Found
I found some existing issues that might be related. Please check if any of these are duplicates or contain helpful solutions:
🟢 #296 Message sent during a failed stream silently forks the chat into two concurrent branches with tool access
This is the closest match conceptually: both reports describe branch corruption caused by relying on message done-state instead of the true running task state. Issue #296 is the send-side version, while #298 is the injection-side version when background completions are attached during an active turn.
by Classic298
🟢 #220 bug: Chat ends when Claude Code agent spawns a background agent and never resumes
This issue reports that background agents can finish without the chat resuming, which overlaps with the same background-agent lifecycle and resume logic involved in #298. It is related, though #298 goes deeper into how completions are attached and can fork or strand turns.
by JKGeovision
🟢 #213 bug: a stalled agent session hangs the chat task forever, and the whole transcript is lost if you kill it
This issue is about stalled sessions leaving
done=0turns hanging forever, which aligns with #298's 'zombie' not-done assistant state poisoning future injections. The symptoms differ, but both center on a non-terminal turn blocking normal chat progress.by ThalynLabs
💡 If your issue is a duplicate, please close it and add any additional details to the existing issue instead.
This comment was generated automatically. React with 👍 if helpful, 👎 if not.