fix(ai): preserve raw provider error payload on responses streams - #44271
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stream failures classified through
OpenResponses.providerFailurelost structured detail beyond a flattened message:codesurvived only as a message prefix, andparam/type/headerswere decoded at the boundary but never consumed by anything.body: stringtoAIErrorcarrying the full raw provider payload, so classified failures never lose the original error detail (mirrorsHttpContext.bodyon the HTTP path).messageorcode, surface the raw payload (JSON string) as the message instead of an invented default; keep the protocol-specific fallback text only for empty payloads.reasoning.donevalidation of events the parser ignored, single-use aliases (hostedToolItemID), constantmessagePhasefunction state onParserState, and theRefusalEventschema union replaced with direct field checks.Test plan
falls back to a stable defaulttests inopenai-responses.test.tsto pin the new contract (raw-payload message +error.body === error.reason.message).bun testinpackages/ai: only pre-existing OpenRouter cassette failures remain (verified identical on cleanv2).bun run typecheckpasses inpackages/aiandpackages/core.