Skip to content

fix(opencode): retry transient network errors instead of surfacing as terminal with raw content - #31440

Closed
Sylchi wants to merge 1 commit into
anomalyco:devfrom
Sylchi:fix/retry-transient-network-errors
Closed

fix(opencode): retry transient network errors instead of surfacing as terminal with raw content#31440
Sylchi wants to merge 1 commit into
anomalyco:devfrom
Sylchi:fix/retry-transient-network-errors

Conversation

@Sylchi

@Sylchi Sylchi commented Jun 9, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #31133, closes #20822, closes #15350, closes #21893

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When a network request drops mid-flight (ECONNRESET, ECONNREFUSED, fetch failure, premature close, etc.), the error was classified as NamedError.Unknown and never retried — leaving the raw error body (potentially an HTML error page or thousands of characters) dumped on screen.

Two changes:

  1. Transport error detection in MessageV2.fromError() — Added isTransientNetworkError() helper that catches SystemError codes (ECONNRESET, ECONNREFUSED, ETIMEDOUT, ENOTFOUND, EAI_AGAIN, ENETUNREACH, EPIPE), TypeError from fetch, and common transient message patterns. These now produce an APIError with isRetryable: true so SessionRetry retries instead of surfacing a terminal error.

  2. Truncate long error response bodies in ProviderError.message() — When a proxy/gateway returns an HTML error page or any long non-JSON body, e.responseBody was embedded verbatim. Now truncated at 200 chars with "..." suffix.

How did you verify your code works?

  • Typecheck passes (bun typecheck in packages/opencode)
  • All 33 retry classification tests pass
  • All 4 account CLI tests pass
  • Full repo typecheck passes (23/23 packages)

Screenshots / recordings

N/A

Checklist

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

… terminal with raw content

When a network request drops mid-flight (ECONNRESET, ECONNREFUSED, fetch
failure, premature close, etc.), the error was classified as
NamedError.Unknown and never retried, leaving a large raw error body on
screen.

Two changes:

1. Add isTransientNetworkError() detection in MessageV2.fromError() that
   catches SystemError codes (ECONNRESET, ECONNREFUSED, ETIMEDOUT, ...),
   TypeError from fetch, and common transient message patterns. These now
   produce an APIError with isRetryable: true so SessionRetry retries.

2. Truncate e.responseBody in ProviderError.message() at 200 chars to
   prevent dumping long HTML error pages or raw content into the visible
   error message.

Closes anomalyco#31133, anomalyco#20822, anomalyco#15350, anomalyco#21893
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on the search results, I found one potentially related PR:

PR #30638 - fix(session): classify transport and timeout errors as retryable

The other results (PR #29627 and #19204) are related to error handling and retries but address different specific scenarios (undici TypeError, 5xx status codes) and appear to be distinct from this PR's focus.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jul 9, 2026

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.

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

Projects

None yet

1 participant