fix(review): survive long reviews by bypassing undici bodyTimeout#62
Merged
JohnnyVicious merged 1 commit intomainfrom Apr 14, 2026
Merged
fix(review): survive long reviews by bypassing undici bodyTimeout#62JohnnyVicious merged 1 commit intomainfrom
JohnnyVicious merged 1 commit intomainfrom
Conversation
Long adversarial reviews were failing at ~4.5 minutes with `TypeError: terminated`. Root cause: `sendPrompt` used `fetch()`, and Node's bundled undici imposes a hidden 300_000 ms default `bodyTimeout`. When OpenCode holds the response body open while the model thinks on a large review, undici kills the socket and surfaces the termination as an opaque "terminated" error — even though our outer `AbortSignal.timeout(600_000)` was set to 10 minutes (it's a wall-clock abort, not a dispatcher-level body timer, so it cannot prevent undici's internal kill). Replace the `fetch()` call with a direct `node:http` request via a new `httpPostJson` helper. `node:http` has no equivalent default body timeout, so the request survives until the server responds or our own explicit wall-clock timer fires. The per-call budget is now 30 min, overridable via `OPENCODE_COMPANION_PROMPT_TIMEOUT_MS`, matching the tracked-jobs hard timer. Add a regression test that stalls the response body for 7 seconds to prove we no longer cut long replies off.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TypeError: terminatedsendPromptusedfetch(), which is bound to Node's bundled undici. Undici enforces a 300_000 ms defaultbodyTimeoutand kills the socket the moment the OpenCode server holds the response body open for >5 min while the model thinks — which is the normal case for long reviews on slow/free models. Our outerAbortSignal.timeout(600_000)is a wall-clock abort, not a dispatcher-level body timer, so it cannot prevent that kill.fetch()call with a directnode:httpPOST via a newhttpPostJsonhelper.node:httphas no equivalent hidden default, so the request survives until the server responds (or our own explicit wall-clock timer fires).OPENCODE_COMPANION_PROMPT_TIMEOUT_MS, matching the tracked-jobs 30-min hard timer.Repro / evidence
Reproduced the exact error locally:
The server sent headers + a single body byte, then stalled. Undici destroyed the socket at its 300 s default, and
fetch()surfaced the cause asTypeError: terminated— byte-for-byte the message the failing review logged.Test plan
tests/send-prompt-body-timeout.test.mjsstalls the response body for 7 s and asserts the client tolerates it. Would have failed against the oldfetch()-based implementation if its timeout were lowered below 7 s.npm test→ 222/222 pass.