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
SessionProcessor.process() can deadlock or leave sessions in a permanently running state in two cases:
The stream loop uses for await (const value of stream.fullStream). If abort fires while iter.next() is waiting on a stuck tool result, iteration can block indefinitely.
Cleanup-time Snapshot.patch() is outside the main error boundary. If it throws, message finalization can be skipped, leaving sessions/tool parts stuck as running.
A related symptom is doom-loop permission prompts blocking cancellation when they are awaited without racing abort.
Plugins
None
OpenCode version
1.3.3 (also observed on dev as of 2026-03-30)
Steps to reproduce
Start a session that triggers a long-running or hanging tool call.
Cancel while the model stream is waiting for tool output.
Observe session remains in cancelling/running state.
Separately, force Snapshot.patch() failure during cleanup (for example by invalidating git state) and observe finalization is skipped.
Cross-reference for triage: this issue is narrowly scoped to processor stream-loop abort behavior and cleanup-finalization safety. Related but distinct: #20095 (cancel lifecycle + dangling waits), #15080 (task timeout feature), #20099 (runtime watchdog).
This commit replaces the raw for await loop over the LLM stream with Effect/Stream (Stream.tap → Stream.runDrain). Stream interruption is now native via Fiber.interrupt — when Runner.cancel interrupts the processing fiber, the stream drain is interrupted immediately without waiting for iter.next() to resolve. The cleanup() function handles all dangling state (currentText, reasoningMap, pending tool parts), preventing zombie sessions.
The Runner module introduced in c5442d418d — refactor(session): effectify SessionPrompt service (#19483) (Mar 30) — further solidifies this by providing a clean cancel → Fiber.interrupt pipeline.
Activity
github-actions commentedon Mar 30, 2026
This issue might be a duplicate of existing issues. Please check:
PermissionNext.ask()/Question.ask()prompts blocking cancellation is also reported there ("Dangling permission/question prompts after cancel" section)If these are intentionally split reports, it may help to cross-reference them to assist maintainers in triaging.
[-]Stream iteration hangs on abort; cleanup failure causes zombie sessions[/-][+][BUG] Stream iteration hangs on abort; cleanup failure leaves zombie sessions[/+]ESRE-dev commentedon Mar 30, 2026
Cross-reference for triage: this issue is narrowly scoped to processor stream-loop abort behavior and cleanup-finalization safety. Related but distinct: #20095 (cancel lifecycle + dangling waits), #15080 (task timeout feature), #20099 (runtime watchdog).
ESRE-dev commentedon Apr 1, 2026
Closing: Resolved by upstream Effect/Stream migration
The stream iteration hang on abort described in this issue is resolved by the upstream migration from raw
for awaitto Effect/Stream.Upstream commit
860531c275—refactor(session): effectify session processor (#19485)(Mar 28)This commit replaces the raw
for awaitloop over the LLM stream withEffect/Stream(Stream.tap→Stream.runDrain). Stream interruption is now native viaFiber.interrupt— whenRunner.cancelinterrupts the processing fiber, the stream drain is interrupted immediately without waiting foriter.next()to resolve. Thecleanup()function handles all dangling state (currentText, reasoningMap, pending tool parts), preventing zombie sessions.The
Runnermodule introduced inc5442d418d—refactor(session): effectify SessionPrompt service (#19483)(Mar 30) — further solidifies this by providing a cleancancel→Fiber.interruptpipeline.See also: #20095 (parent issue, also closed)