Skip to content

fix(session): retry HTTP 408 request timeouts - #47524

Open
Hotragn wants to merge 1 commit into
anomalyco:devfrom
Hotragn:retry-request-timeout
Open

fix(session): retry HTTP 408 request timeouts#47524
Hotragn wants to merge 1 commit into
anomalyco:devfrom
Hotragn:retry-request-timeout

Conversation

@Hotragn

@Hotragn Hotragn commented Sep 5, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #47525

Type of change

  • Bug fix

What does this PR do?

Replaces #39413, which was closed by the automated cleanup rather than by a fix. Rebased onto the reworked retry.ts (message patterns, jitter, max retries) and re-verified against current dev.

retryable() only bypasses the provider SDK's isRetryable flag for status >= 500, so an HTTP 408 that the SDK didn't mark retryable ends the turn and the user has to resend the prompt by hand. This shows up with OpenAI-compatible proxies that normalise an aborted upstream stream into 408 request_timeout.

Note on the newer RETRYABLE_MESSAGE_PATTERNS: they don't cover this case. The timeout pattern requires a literal space (request timeout), while the payload in #39221 carries request_timeout with an underscore. I checked the reported message and responseBody against the current pattern list and both return false, so a 408 still falls through today.

408 Request Timeout is the one 4xx that is transient in the same way a 5xx is — the request never completed, so sending it again is the defined behaviour for that status. It takes exactly the same path as 5xx: same classification, same backoff, same retry-after handling. Other 4xx statuses are unchanged.

#39221 was closed as addressed by #39391, which fixed 408/409 in packages/ai (the v2 path). This targets the v1 packages/opencode/src/session path, which is still wired in through session/processor.ts and still classifies these as non-retryable. Happy to close this if that path is being retired.

How did you verify your code works?

retries 408 request timeout errors in test/session/retry.test.ts, built from the payload in the issue. Ran the whole suite — 61/61 pass, including does not retry 4xx errors when isRetryable is false (status 400), so 4xx isn't loosened generally. bun typecheck clean.

Checklist

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

retryable() only bypasses the provider SDK's isRetryable flag for status
>= 500, so a 408 the SDK did not mark retryable ended the turn and the
user had to resend the prompt. OpenAI-compatible proxies that normalize
an aborted upstream stream into 408 request_timeout hit this.

408 is the one 4xx that is transient in the same way a 5xx is: the
request timed out before completing, so it is safe to send again. Treat
it exactly like the existing 5xx case, same backoff and retry-after
handling. Other 4xx statuses are unchanged.

Closes anomalyco#39221
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP 408 is not retried on either path; 409 regressed since #39391

1 participant