Skip to content

fix(ai): preserve raw provider error payload on responses streams - #44271

Merged
rekram1-node merged 2 commits into
v2from
stream-error-body
Aug 23, 2026
Merged

fix(ai): preserve raw provider error payload on responses streams#44271
rekram1-node merged 2 commits into
v2from
stream-error-body

Conversation

@rekram1-node

Copy link
Copy Markdown
Collaborator

Summary

Stream failures classified through OpenResponses.providerFailure lost structured detail beyond a flattened message: code survived only as a message prefix, and param/type/headers were decoded at the boundary but never consumed by anything.

  • Add an optional body: string to AIError carrying the full raw provider payload, so classified failures never lose the original error detail (mirrors HttpContext.body on the HTTP path).
  • When a stream error carries no usable message or code, surface the raw payload (JSON string) as the message instead of an invented default; keep the protocol-specific fallback text only for empty payloads.
  • Remove dead code found along the way: no-op reasoning.done validation of events the parser ignored, single-use aliases (hostedToolItemID), constant messagePhase function state on ParserState, and the RefusalEvent schema union replaced with direct field checks.

Test plan

  • Updated the three falls back to a stable default tests in openai-responses.test.ts to pin the new contract (raw-payload message + error.body === error.reason.message).
  • bun test in packages/ai: only pre-existing OpenRouter cassette failures remain (verified identical on clean v2).
  • bun run typecheck passes in packages/ai and packages/core.

Stream failures classified through OpenResponses.providerFailure lost all
structured detail beyond a flattened message: code survived only as a
message prefix, and param/type/headers were decoded but never consumed.

- carry the full raw payload as a string on AIError.body
- fall back to the raw payload (JSON string) when the provider supplies
  no usable message or code, instead of an invented default
- drop dead validation branches (reasoning.done item_id checks on events
  the parser ignored), inline single-use aliases, and remove constant
  messagePhase state from ParserState
- replace the RefusalEvent schema union with direct field checks
Classification previously only saw the summary message on the stream path,
so overflow phrases or nested codes living elsewhere in the error payload
were missed. Feed the raw body into classifyProviderFailure as additional
scan text alongside the message.
@rekram1-node
rekram1-node merged commit 92658e4 into v2 Aug 23, 2026
6 of 7 checks passed
@rekram1-node
rekram1-node deleted the stream-error-body branch August 23, 2026 02:00
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.

1 participant