Repository navigation
Description
Summary
When a configured openai-compatible provider's baseURL points to a port with nothing
listening (TCP ECONNREFUSED — e.g. a local llama.cpp server that is not started yet, or
crashed), opencode run produces no output at all and never exits. No error, no retry
notice, no timeout — from the outside it is indistinguishable from a deadlock (~50 Bun
threads parked in futex, zero open sockets, because the refused socket closes instantly
and the event loop sleeps on the backoff timer 29.9 s out of every 30).
A live-but-slow endpoint works fine. The trigger is strictly connection refused.
Reproduced on 1.17.4, 1.17.13, 1.17.14 and 1.18.11, with a minimal isolated config
(fresh XDG_CONFIG_HOME/XDG_DATA_HOME, a single custom provider, no plugins).
Reproduction (no GPU, ~30 seconds)
ISO=$(mktemp -d); mkdir -p $ISO/opencode $ISO/data
cat > $ISO/opencode/opencode.json <<'EOF'
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"local": {
"npm": "@ai-sdk/openai-compatible",
"name": "local repro",
"options": { "baseURL": "http://127.0.0.1:59999/v1" },
"models": { "m": { "name": "local repro" } }
}
}
}
EOF
time timeout 40 env XDG_CONFIG_HOME=$ISO XDG_DATA_HOME=$ISO/data \
opencode run -m local/m "say pong"
# → 40s, zero bytes of output, killed by `timeout` (exit 124). Expected: a prompt,
# clear error within seconds ("cannot connect to http://127.0.0.1:59999/v1").Root cause (from reading v1.18.11 source)
@ai-sdk/provider-utilshandleFetchErrormarks every network-level failure
(includingECONNREFUSED) asAPICallError { isRetryable: true }.- opencode propagates that flag untouched:
packages/opencode/src/session/message-v2.ts:676-704
andpackages/opencode/src/provider/error.ts:181. There is no branch for
ECONNREFUSED/ENOTFOUND/EHOSTUNREACH(contrast with the explicitECONNRESET
handling atmessage-v2.ts:625-637). - The retry policy never gives up:
packages/opencode/src/session/retry.ts:68-79
(retryable()always returns an object whileisRetryableis true) and
retry.ts:176-200(policy()— theSchedulehas noSchedule.recurs, no time
budget; only the delay is capped atRETRY_MAX_DELAY_NO_HEADERS = 30_000,
retry.ts:26-28). Net effect: one attempt every 30 s, forever. packages/opencode/src/session/processor.ts:660-675applies
Effect.retry(SessionRetry.policy(...))beforeEffect.catch(halt)— sohalt
is unreachable,session.erroris never published, the session never reachesidle.- Non-interactive mode has no exit path and no feedback:
packages/opencode/src/cli/cmd/run.ts:789-793only breaks on
session.status === "idle", and theretrystatus events (emitted at
processor.ts:664-672) are consumed by the TUI
(packages/tui/src/routes/session/index.tsx:352) but nowhere inrun.ts— not
even with--format json. Meanwhilerun.ts:862-869awaits
client.session.prompt(...)with no client-side timeout. - No workaround via options: the fetch wrapper honors
timeout/headerTimeout/
chunkTimeout(packages/opencode/src/provider/provider.ts:1737-1768) but those only
arm on an established connection —ECONNREFUSEDfails before them — and
headerTimeoutgets a default only for theopenaiprovider (provider.ts:208).
opencode runhas no--timeout/--max-retriesflag (run.ts:128-210).
Why it matters
Anyone driving a local model server (llama.cpp, vLLM, Ollama-compatible endpoints)
hits this whenever the server is down or restarting: batch/CI usage of opencode run
freezes forever with zero diagnostics. We have been wrapping every invocation in our own
TCP pre-flight probe + external watchdog to survive it.
Suggested fixes (any subset helps)
- Treat
ECONNREFUSED/ENOTFOUND/EHOSTUNREACHas non-retryable (or retry a small,
bounded number of times, then fail with the underlying message). - Bound the retry schedule (max attempts and/or total time budget).
- Surface the
retrystatus inrunmode (both plain and--format json) so the user
can at least see what is happening. - Expose
--max-retries/--timeoutonopencode run.
Related: #17516 (opencode run hangs after completing tool calls) is a different hang
(post-completion), but both share the "non-interactive mode can wait forever with no
output" trait.
Activity
github-actions commentedon Aug 3, 2026
This issue might be a duplicate of existing issues. Please check:
opencode runretries indefinitely against an unreachable local provider (ECONNREFUSED) with no visible error or exit, same version (1.18.11).Additionally, #39790 covers a related problem with the same unbounded
SessionRetry.policy()— retrying forever on fixed-window quota errors (HTTP 429) — if that analysis is also relevant context.Martossien commentedon Aug 3, 2026
Duplicate of #40319 (filed a few hours earlier, same symptom, same version). Moved the root-cause analysis there: #40319 (comment) — the 30-second repro script in this issue's description remains valid.