Skip to content

Transport resilience: spurious fetch failed on long turns + no result recovery #2

Description

@TheRealDinghyDog

Transport resilience: spurious fetch failed on normal-length turns + no result recovery

Priority: high. Surfaced while smoke-testing the OpenCode subagent flow on PR #1; root-cause diagnosis by Claude Code, plus related self-review findings #16/#17.

Symptom

A ~6-minute adversarial review completed successfully server-side (ses_0c072d6d…, log shows exiting loop, no server error), but the companion returned TypeError: fetch failed and the job was recorded as failed with no output. The findings (454 KB) were fully recoverable via GET /session/{id}/message on the still-running server.

Root cause

captureTurn (lib/opencode.mjs) drives each turn over two long-lived HTTP connections to the shared server: an SSE event subscription and the client.sendMessage POST (opencode-server.mjs), which is held open for the entire turn and awaited in Promise.all([state.completion, responsePromise]). Node/undici defaults to a ~300s headers/body timeout. A normal deep review (single streaming step stalled ~3m37s; ~5m50s total) exceeds that budget → fetch failed, even though the server turn is healthy.

Compounding gap: no recovery path

  • handleResult (opencode-companion.mjs) reads the local job file only — it never re-fetches from the server, so a completed-but-uncaptured turn is unrecoverable via result.
  • runServerTurn discards the already-completed server turn on any client-side throw.

Related self-review findings

Suggested direction

  1. On a client-side fetch failed/timeout, re-fetch the session's final message over HTTP (GET /session/{id}/message) before declaring failure.
  2. Give result a server-side fallback so result <job> transparently recovers completed turns.
  3. Stop relying on a single long-held sendMessage fetch: prefer prompt_async + SSE/poll as the source of truth, and/or tune undici timeouts and add an explicit outer turn timeout.

Ref: PR #1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions