Repository navigation
Feat: Add HTTPResponseError for HTTP failures, including streaming - #490
Closed
luis-silva-ms wants to merge 2 commits into
Closed
luis-silva-ms wants to merge 2 commits into
luis-silva-ms wants to merge 2 commits into
Conversation
HTTP status failures (>= 400) from JSON, raw-data and streaming requests now throw a single HTTPResponseError carrying the status code, the HTTP response, the raw body (truncated for streams), the decoded API error when available and any underlying transport error. Streaming failures now run response middleware, skip SSE middleware for error bodies, cap the collected error body, and rethrow cancellation unchanged.
Author
|
Superseded by #491, which uses a |
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.
TL;DR: HTTP failures (status >= 400) from JSON, raw-data and streaming requests now throw a single
HTTPResponseError. It carries the status code and the raw body, so callers can read error JSON that the SDK doesn't decode, such as a gateway's own error format. It also carries the response and the decoded API error when there is one. Streaming failures now also go through response middleware.There is no source break, but behaviour changes at runtime: code that catches
APIErrorResponse,GeminiAPIErrorResponseorOpenAIError.statusErrorfor HTTP failures no longer matches them.What
HTTP status failures (status >= 400) now throw a single public error type,
HTTPResponseError, for JSON, raw-data and streaming requests.HTTPResponseErrorexposes:statusCodeandresponse(HTTPURLResponse)body: the raw error body (Data), kept even when the SDK can't decode it: for example a gateway's custom error JSON, or a non-JSON bodyisBodyTruncated: streaming error bodies are capped at 256 KiBapiError: the decodedAPIErrorResponse/GeminiAPIErrorResponsewhen the body is oneunderlyingError: a transport error that arrived with the failing responseStreaming (
StreamingSession) changes:CancellationError/URLError.cancelled) is rethrown as is, not wrapped.Non-streaming (
URLResponseHandler): keeps #471's flow (middleware first, status read from the middleware-returned response). It now throwsHTTPResponseErrorinstead ofAPIErrorResponse/GeminiAPIErrorResponse/OpenAIError.statusError. Cancellation is rethrown first.Also updated: README ("HTTP failures" section), CHANGELOG (Unreleased), and the Demo's 400/401/429 hints in
ResponsesStore.Why
When a request fails at the HTTP level, callers often can't read the error:
{"error":{"code":"…","message":"…"}}withouttype, or a flat{"code":"…","message":"…"}.APIErrorrequirestype, so decoding fails. The caller then getsOpenAIError.statusErrorwith only the status code, and thecodeandmessageare lost. Non-JSON bodies (HTML, plain text) are lost the same way.One error type with the status code and the raw body lets callers decode their own error JSON, on streaming and non-streaming requests alike. We need this in a production iOS app that calls OpenAI through an API gateway. The app reads:
codeandmessage, to detect quota errors and show a message.Related: #287 (streaming status errors), #469 / #471 (non-streaming status check), #484 (single Gemini error object, not addressed here).
There is no source break:
swift package diagnose-api-breaking-changes 0.6.0reports no breaking changes. Only a public type is added.It is a runtime behaviour change. Code that catches
APIErrorResponse,GeminiAPIErrorResponseorOpenAIError.statusErrorfor HTTP failures will silently stop matching. It should catchHTTPResponseErrorand readapiError/statusCodeinstead. Errors sent inside a successful (2xx) stream keep their existing types.I'm opening this as a draft to check the direction first. If you'd rather avoid the behaviour change, an additive variant is possible. For example, keep the current thrown types and attach the raw body and response to them. I'm happy to rework it.
Affected Areas
Sources/OpenAI/Public/Errors/HTTPResponseError.swift: new public typeSources/OpenAI/Private/URLResponseHandler.swift: non-streaming error pathSources/OpenAI/Private/Streaming/StreamingSession.swift: streaming error pathURLResponseHandlerTests; extendedStreamingSessionErrorHandlingTestsandMockMiddleware.JSONResponseStatusTests,FilesEndpointTestsandOpenAITestsupdated to expectHTTPResponseError.ResponsesStorestatus hints)Validation
swift build: ✅ passedswift test: ✅ passed (277 XCTest, 0 failures; 35 Swift Testing tests passed)xcodebuild -project Demo/Demo.xcodeproj -scheme Demo -destination "generic/platform=iOS Simulator" build: ✅ BUILD SUCCEEDEDswift package diagnose-api-breaking-changes 0.6.0: ✅ No breaking changes detected in OpenAIHTTPURLResponse/URLRequestimportFoundationNetworkingunder#if canImport(FoundationNetworking).