Skip to content

Sessions stall in a loop of ~4-token no-message completions (claude-sonnet-5-5 via claude-code provider) #1903

Open

Description

@dezren39

Observed defect: sessions stall in a loop of ~4-token no-message completions (claude-sonnet-5-5 via claude-code provider)

Observed twice on 2026-11-06 evening (CST), production server (fork build 08eca39), during the fleet feature wave on issue #1885:

  • Session ses_e5e4c6d3d7dffegQw6FgFK7K2u (route specialist, provider claude-code, model claude-sonnet-5-5, variant high): after ~40 productive minutes it began emitting route attempts with tokens_output = 4, assistant_message_id = NULL, outcome success, recurring roughly every ~4 minutes (route_attempt ids 323432, 323500; tokens_input ~110k). No new session messages, no tool calls; a minder "Continue." prompt was accepted (HTTP 200) and also produced only a 4-token no-message attempt. Session was interrupted by the minder.
  • Successor session ses_e5e4c5f4065ffeGMJc7943Q5sG (same route/model, fresh context, ~50k input tokens): three productive attempts, then the same 4-token no-message pattern and stall. Interrupted by the minder.
  • Control group: two sessions on the workhorse route (DeepSeek) ran 60+ minutes the same evening with no such stall; the feat: API launch mode that disables the question tool entirely for detached / non-interactive sessions #1885 stream was relaunched on workhorse and proceeded normally.

Why it matters: a session in this state looks merely quiet — it burns a full-context attempt every few minutes and never progresses, and the retry/continuation machinery does not surface it as an error (outcome is recorded as success). It is adjacent to #1891 (serving-leaf invisibility) but distinct: the failure is in the attempt loop, not observability.

Suggested investigation: what a 4-token completion with no assistant message contains (stop reason?), why the loop keeps scheduling continuations after empty completions, and whether an empty-completion streak should trip the availability/cascade machinery (treat N consecutive no-message completions as a failed attempt and fail over).

Evidence: route_attempt rows for the two sessions above in the laptop server store (~/.local/share/opencode/opencode.db), 2026-11-07T00:50Z–01:45Z.

Acceptance criteria

  • A claude-code-provider session that receives a no-message ~4-token completion does not loop indefinitely: the loop is detected (repeated no-message completions) and the session surfaces an error or recovers instead of silently stalling
  • Route-attempt records for the stall pattern are distinguishable from genuine short completions (a marker, outcome, or message-id invariant is enforced or logged)
  • Regression coverage reproduces the observed pattern (successive 4-token, NULL-message attempts) against the session loop

Activity

  1. developing-today commented on Nov 7, 2026

    @developing-today

    Triage evidence — CONFIRMED by reading current main (issue-sweep-rotating)

    Checked 2026-11-07T05:45Z against main cfc5e3d82cd2e9c0d6e304cdc6644218f26d8905 (fresh gh api raw reads, verified twice). No local repro run in this feeder pass — the production evidence is the two session IDs and route_attempt rows named on the issue (laptop server store, 2026-11-07T00:50Z–01:45Z); the code read below shows why that pattern is possible and invisible:

    • A no-message completion is recorded as success. packages/core/src/session/runner/step.ts:584-590 — the attempt row's outcome is "interrupted" / "timeout" / "failure" only when the stream interrupted, timed out, failed, or hit a policy finish; every other completion — including one that produced no assistant message — falls through to "success". tokens_output is recorded verbatim (step.ts:607), so a 4-token no-message attempt is indistinguishable from a productive one in outcome terms.
    • No empty-completion guard exists in the runner. Grep for empty over packages/core/src/session/runner/step.ts, runner/retry.ts, and runner/llm.ts on current main: zero matches in all three files (verified twice). Nothing counts consecutive no-message completions, nothing treats an empty streak as a failed attempt, and the failover/cascade gate (step.ts:692) keys only on llmFailure — which a "successful" empty completion never sets.
    • Scope check: the control group on the issue (DeepSeek workhorse sessions, same evening, no stall) plus the provider named (claude-sonnet-5-5 via claude-code) suggests the trigger may be provider-specific, but the missing guard is provider-agnostic: any leaf returning tiny no-message completions loops the same way.

    What "done" looks like (from the issue, made testable): N consecutive no-message completions for a session are treated as a failed attempt (failover/cascade engages, or the turn ends with a visible error) instead of an unbounded series of success rows; a single empty completion followed by a productive one does not trip it. Tests name their oracle: a scripted provider returning empty completions — assert attempt outcomes and that the loop terminates.

    Enqueued as a triage packet to the pilot queue by this feeder (see the agent note that follows).

  2. developing-today commented on Nov 7, 2026

    @developing-today

    Enqueued by issue-sweep-rotating (triage feeder) as queue task 11037 (repo-health-opencode-pilot, kind triage, priority 70, dedupe repo-health:v1:triage:developing-today/opencode:issue:1903). Triage evidence is in the comment above (6702970740): a no-message completion records outcome=success at runner/step.ts:584-590, and no empty-completion guard exists in the runner (grep empty = 0 in step/retry/llm). The queue/queued label is applied in the same pass.

  3. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    source/humanFiled directly by a human
    priority/highImportant and time-sensitive
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 7, 2026
  4. dezren39 commented on Nov 7, 2026

    @dezren39
    Author

    Intake review

    Related: filed from the 2026-11-06 feature wave on issue #1885, where two sessions stalled and a minder 'Continue.' prompt did not break the loop; the DeepSeek workhorse control group ran 60+ minutes clean, which points at the claude-code provider path rather than the session machinery in general. Risks: a blunt retry cap could kill legitimately terse completions — detection should key on the no-message property, not token count alone. Suggested approach: instrument the attempt loop for consecutive no-message completions, then decide between provider-level retry-with-backoff and session-level escalation using the recorded attempt ids in the issue.

    — Fleet monitor intake (repo-fleet-monitor, 2026-11-07 run): acceptance criteria appended to the issue body, labels normalized (kind/bug, source/human, priority/high, effort/m, status/needs-triage, meta/enriched), marked meta/enriched. No merged PR or commit was found this run that explicitly resolves this issue, so it stays open.

  5. added
    work/assignedAn agent holds this item via the queue (claimed task)
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 9, 2026
  6. dezren39 commented on Nov 9, 2026

    @dezren39
    Author

    Claimed queue task 11037 on lane 3fe37e8a-c61b-419c-9e97-769ecd264d5d. Fresh GitHub check: issue #1903 is open, no matching PR exists, and no hard-stop labels are present. Validating packet consistency before any code work.

  7. added
    queue/blockedQueue task is blocked (reason recorded on the task)
    status/blockedBlocked on something named in the issue/PR
    and removed
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 9, 2026
  8. dezren39 commented on Nov 9, 2026

    @dezren39
    Author

    Queue task 11037 was blocked as packet_defect after fresh verification. The packet is labeled kind=triage and says triage is confirmed, but its acceptance criteria and scope require implementation of the empty-completion guard plus regression tests. No code or worktree was created. Please correct/re-enqueue this as an implementation task; the queue claim is released.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/mMedium: expected under 1 day of workkind/bugSomething isn't working (prefixed form of the classic bug label)meta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivequeue/blockedQueue task is blocked (reason recorded on the task)source/humanFiled directly by a humanstatus/blockedBlocked on something named in the issue/PR

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions