Skip to content

SessionProcessor accepts incomplete streams, replays tool turns on retry, and repeats overflow recovery #48454

Closed as not planned

Description

@yixiao5428

Version: dev @ 193de13a88

Symptoms (all reproducible on current dev):

  1. Incomplete streams are accepted as settled. When a provider stream ends with finish other and no raw reason (or the connection just ends after a settled step), the turn is treated as complete. Expected: the turn should fail closed with a typed error so the caller/retry layer can react.
  2. Retries replay turns that already had side effects. After a retryable error, the processor re-issues a request even when the attempt already executed tool calls or dispatched experimental.text.complete hooks, and stale parts/accounting of the failed attempt linger in the transcript.
  3. Interruption/failure identity is lost. A cancellation landing during a retry backoff (or a cleanup write fault) surfaces as a plain error or a fake success; the original interrupt cause is not preserved for the caller.
  4. Overflow recovery is unbounded. Main overflow → compaction → replayed main → overflow again triggers another compaction; there is no per-run limit, no admission check for compressible history, and no veto once the attempt already produced output.
  5. A failed turn without finish restarts on loop re-entry, issuing a new provider request instead of returning the stored failure (a turn with finish: "error" correctly stops).

Expected: bounded, vetoed retry with attempt rollback; interruption-preserving cancellation; typed incomplete/length/overflow classification; one reactive recovery per prompt-loop run; failed turns stop on re-entry.

We have a PR ready with regression coverage for each case — will link it as soon as the issue number exists.

Activity

  1. satwareAG-ironMike commented on Oct 4, 2026

    @satwareAG-ironMike

    Independent confirmation of the "replays tool turns on retry" leg of this report, with log evidence from a production incident (2026-10-04, opencode 2.0.22, Linux x64, serve --service + TUI topology):

    • A long-lived session hitting session_message insert failures (UNIQUE(session_id, seq) - context in session: two server processes sharing one opencode.db allocate session_message.seq independently - UNIQUE(seq) collisions fail sessions #53146) drained/retried turns at ~40-70 s cadence.
    • Each retry re-executed the failed turn's pending bash tool calls. The tool call was a forge issue create; the result: 35 duplicate issues in one day, in two bursts, each burst bracketed by Failed to drain Session ERROR entries in the server log.
    • Key point from the operator side: this layer is below agent reasoning. The agent never received a result between retries, so no prompt-level discipline (search-before-create, verification) can intervene. Only idempotent wrappers on the tool side contained the blast radius (harness#1168 has the full RCA + cleanup).
    • Expected behavior matches this issue: a retried turn must not re-execute tool calls whose execution already started or completed; record intent before dispatch or mark side-effectful calls non-replayable and fail the turn explicitly so the agent can observe the failure.

    Full evidence trail: satware.ai/harness#1168 (RCA, sanitized log excerpts available on request), triggered by #53146.

  2. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up, and for preparing regression coverage.

    The commit you reference (193de13a88) is from before v2 was promoted to dev. The SessionProcessor in packages/opencode/src/session/processor.ts is the v1 implementation (that package is version 1.18.30 at that commit). v1 only gets fixes for critical bugs (data loss, security problems, or crashes with no workaround), so these retry, overflow and stream-classification changes are unlikely to be merged there.

    On current dev, OpenCode v2 (@opencode/cli) is a separate implementation. Session running now lives in packages/core/src/session/ (runner/retry.ts, runner/step.ts, compaction.ts), and its retry path lets plugin hooks veto a retry. I'd encourage upgrading to v2. If any of these behaviours (for example, tool calls replayed on retry or repeated compaction after an overflow) also happen on v2, please reply with the v2 version and a short description of what you saw, and we'll take another look. Please also reply if you think one of these is critical for v1.

  3. opencodejr commented on Oct 29, 2026

    @opencodejr
    Contributor

    Closing this as not planned. The SessionProcessor behaviour described here is in the v1 implementation (packages/opencode, 1.18.30 at 193de13a88), and v1 only receives fixes for critical bugs. We waited in case any of these problems also happen on v2, but we haven't heard back.

    If you see any of them on OpenCode v2 (@opencode/cli), please open a new issue with the v2 version and a short description of what happened, and link back to #48454. Thanks again for the thorough analysis.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions