Skip to content

Retryable error: bun fetch socket drop not classified ("socket connection was closed") #6431

Description

@DeviosLang

What do you want to change?

isRetryableAssistantError (packages/ai/src/utils/retry.ts) does not classify bun's fetch socket-drop error as retryable, so pi terminates on a transient network drop without retrying once when running on the bun runtime.

Why?

Repro: pi --mode json on oven/bun:1-alpine, provider = tencentmaas (openai-compatible), mid-stream the upstream socket closes. The final message_end event:

stopReason: "error"
errorMessage: "The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()"

Result: willRetry=false, agent ends immediately, 0 retries. The retry settings are enabled (default maxRetries=3).

Root cause — RETRYABLE_PROVIDER_ERROR_PATTERN lists Node/undici wording ("socket hang up", "fetch failed", "other side closed") but not bun's wording. I confirmed by running the current regex against the error string: no match → classified non-retryable. On Node the same physical socket drop surfaces as "fetch failed" / "socket hang up" and does retry; on bun it surfaces as "socket connection was closed unexpectedly" and does not.

This silently fails long-running headless jobs — the pi process exits cleanly (exit 0) because the error was "handled", but no work was produced.

How?

Add one pattern to RETRYABLE_PROVIDER_ERROR_PATTERN in packages/ai/src/utils/retry.ts, in the network/fetch block alongside "socket hang up":

"socket connection was closed",   // bun fetch wording (vs Node's "socket hang up" / "fetch failed")

Plus a test in packages/ai/test/retry.test.ts using the real error string above. Happy to open a PR if lgtm.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions