fix(opencode): classify OpenRouter's typeless 5xx chunks as retryable APIError
Description
Follow-up to #43 (USE-2673). That PR handled the one typeless OpenRouter chunk that meant context overflow. OpenRouter uses the same shape for every relayed upstream failure, and in the last 30 days production saw three more of them, each ending a turn as a non-retryable UnknownError:
{ "code": 502, "message": "Stream ended before a terminal response event", "metadata": { "error_type": "provider_unavailable" } }
{ "code": 503, "message": "upstream connect error or disconnect/reset before headers. reset reason: connection timeout" }
{ "code": 504, "message": "Upstream idle timeout exceeded", "metadata": { "error_type": "timeout" } }
These are transient provider failures. The session retry loop already retries any APIError marked retryable or carrying a 5xx status, but parseStreamError never produced one for a body without type: "error".
This PR adds one rule after the overflow check: a typeless body whose numeric code is 5xx becomes a retryable APIError carrying the provider's message. The overflow rule stays first so an overflow 502 still compacts rather than retries.
Not shipped to the fleet until a release tag and an OPENCODE_VERSION bump in andytown.
Testing
bun test test/session/message-v2.test.ts: 38 pass, including the new case feeding the exact production 502 chunk throughfromErrorand expecting a retryableAPIError.bun run typecheckinpackages/opencode: clean.
š¤ Generated with Claude Code
Summary by cubic
Classifies OpenRouter's typeless 5xx chunks as retryable APIError so transient provider failures no longer end turns as non-retryable UnknownError.
Bug Fixes
parseStreamErrornow turns any typeless body with a numeric 5xxcodeinto a retryableAPIErrorcarrying the provider's message.- The overflow check stays first, so an overflow 502 still compacts instead of retrying.
- Not deployed until a release tag and
OPENCODE_VERSIONbump in andytown.