Skip to content

[BUG] Stream iteration hangs on abort; cleanup failure leaves zombie sessions #20094

Closed

Description

@ESRE-dev

Description

SessionProcessor.process() can deadlock or leave sessions in a permanently running state in two cases:

  1. 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.
  2. 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

  1. Start a session that triggers a long-running or hanging tool call.
  2. Cancel while the model stream is waiting for tool output.
  3. Observe session remains in cancelling/running state.
  4. Separately, force Snapshot.patch() failure during cleanup (for example by invalidating git state) and observe finalization is skipped.

Screenshot and/or share link

N/A

Operating System

macOS 15.4.1 (repro also seen on Linux CI)

Terminal

Ghostty

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 30, 2026
  2. github-actions commented on Mar 30, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If these are intentionally split reports, it may help to cross-reference them to assist maintainers in triaging.

  3. changed the title [-]Stream iteration hangs on abort; cleanup failure causes zombie sessions[/-] [+][BUG] Stream iteration hangs on abort; cleanup failure leaves zombie sessions[/+] on Mar 30, 2026
  4. ESRE-dev commented on Mar 30, 2026

    @ESRE-dev
    Author

    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).

  5. ESRE-dev commented on Apr 1, 2026

    @ESRE-dev
    Author

    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 await to Effect/Stream.

    Upstream commit

    860531c275 — refactor(session): effectify session processor (#19485) (Mar 28)

    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.

    See also: #20095 (parent issue, also closed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions