Skip to content

fix(provider): apply documented 300s request timeout default - #36650

Closed
FahadBinHussain wants to merge 1 commit into
anomalyco:devfrom
FahadBinHussain:provider-timeout-default
Closed

fix(provider): apply documented 300s request timeout default#36650
FahadBinHussain wants to merge 1 commit into
anomalyco:devfrom
FahadBinHussain:provider-timeout-default

Conversation

@FahadBinHussain

@FahadBinHussainFahadBinHussain commented Jul 13, 2026

Copy link
Copy Markdown

Issue for this PR

Closes#13841

Type of change

  • Bug fix

What does this PR do?

the config docs at https://opencode.ai/docs/config/ list a 300000ms default for provider.<id>.options.timeout, but the option schema is Schema.Record(Schema.String, Schema.Any), so the documented default is never applied — when the user omits timeout, it stays undefined and the if (options["timeout"] !== undefined && ...) guard at packages/opencode/src/provider/provider.ts:1743 skips AbortSignal.timeout entirely.

on top of that, Bun's socket-level timeout is intentionally disabled at provider.ts:1752 (per // @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682), so without an explicit AbortSignal.timeout there's nothing to abort a request whose endpoint stops responding. an LLM call wedged mid-stream hangs the session indefinitely.

the fix is two lines in the custom fetch wrapper: when timeout is undefined, fall back to 300000 before constructing AbortSignal.timeout. false still opts out (the user can pass timeout: false to disable), and an explicit finite value still wins.

constrequestTimeout=options["timeout"]===undefined ? 300000 : options["timeout"]if(requestTimeout!==null&&requestTimeout!==false)signals.push(AbortSignal.timeout(requestTimeoutasnumber))

i understand why this works: AbortSignal.timeout(ms) is what already wires up the abort path for explicit finite timeouts today, so this change just extends that path to the "user didn't set anything" case. the 300000ms value matches the docs and matches what headerTimeout defaults to on OpenAI Codex, so it's not a new invented constant.

How did you verify your code works?

added packages/opencode/test/provider/timeout-default.test.ts — 4 cases mirroring the existing header-timeout.test.ts harness (same testEffect + provideTmpdirInstance + local node:http server):

  • explicit timeout: 50streamText surfaces a DOMException(name="TimeoutError") against a server that never responds
  • explicit timeout: 50 → same abort against a server that delays the response by 100ms
  • timeout: false + fast response → result.text returns "ok" (opt-out honored, no spurious abort)
  • no timeout set + fast response → result.text returns "ok" (the new default doesn't break the happy path)

all 4 pass. also re-ran the existing test/provider/ suite (480 tests across 9 files) and test/session/llm.test.ts + test/session/retry.test.ts (60 tests) — 0 regressions.

the "omitting timeout produces a 5-minute abort against a hung server" path isn't exercised directly because waiting 5 minutes per test case is impractical — the regression-sensitive branch is the same AbortSignal.timeout(requestTimeout) call that the explicit-finite-timeout tests drive, so removing or weakening it would fail those tests.

Screenshots / recordings

n/a — pure backend provider change, no UI.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

The provider option schema stores options under Schema.Record(Schema.String,
Schema.Any) so the documented 300000ms timeout default never reached
AbortSignal.timeout. combined with the intentionally-disabled Bun socket
timeout (bun#16682), this meant LLM requests could hang forever when a
provider endpoint stopped responding.
Fixesanomalyco#13841.
@github-actionsgithub-actionsBot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels Jul 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@github-actions

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

@FahadBinHussain

Copy link
Copy Markdown
Author

this got auto-closed by the cleanup bot, but the bug it fixes is still there. i checked dev just now: provider.ts still does AbortSignal.timeout(options["timeout"]) with no fallback, so omitting timeout still means no abort ever fires and a hung provider wedges the session. #13841 is still open for it.

it's a two-line change and ready to rebase on current dev. mind reopening?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explore subagent hangs indefinitely with Anthropic Claude Opus 4.6 -- no timeout or recovery

1 participant

@FahadBinHussain