fix: return api error instead of decode failure for non-object error bodies - #914
fix: return api error instead of decode failure for non-object error bodies#914andreynering wants to merge 1 commit into
Conversation
|
Sorry for the AI generated PR description. I intended to open it manually but my agent decided to do it itself. Well... Anyway, patch is important. 🙂 In summary, if the server decides to return something that is not JSON, like 502 (gateway timeout), |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 184d3c86f2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
apijson.UnmarshalRoot accepts JSON null for a root struct, so {"error":null} makes UnmarshalJSON return nil and bypasses this fallback. Treat a null/non-object error value as fallback before unmarshalling, and add a {"error":null} regression asserting the typed API error preserves the response status/body.
…bodies When an HTTP error response's body isn't the expected object shape (e.g. a missing, null, or string error value, or a non-JSON document entirely), the SDK returned the raw json.UnmarshalTypeError and dropped the status code and response. Always return *apierror.Error for status >= 400, falling back to the raw body in Message. RawJSON() and Error() fall back to Message so the raw diagnostics are also surfaced through the standard accessors.
184d3c8 to
2bf06cc
Compare
|
I just pushed an update. |
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
Rechecked current 2bf06cc7. error:null now takes the raw-body fallback before unmarshalling, and the same path covers string/non-object and non-JSON bodies while preserving *openai.Error, status, response, and raw body. The structured object case still parses normally. This resolves my earlier concern.
Problem
When an HTTP error response's body is not the expected object shape, the SDK returns the raw
json.UnmarshalTypeErrorinstead of an*apierror.Error:This happens for:
{"error": "you must provide a model parameter"}— a stringerrorvalue, which non-OpenAI-compatible intermediaries sometimes returnThe early
return errininternal/requestconfigalso discards the status code, headers, and response body, so callers can't distinguish a 400 from a 500, and retry decisions keyed offStatusCode(both in the SDK ecosystem and in downstream wrappers) are broken — a 5xx served in an unexpected shape becomes a permanent, unactionable error.Solution
Always return
*apierror.Errorfor status >= 400. When theerrorpayload is missing or doesn't unmarshal into the expected object, fall back to putting the raw response body inMessage(which would otherwise be empty) and return the fully populated error.StatusCode,Request, andResponseare already set on the struct before the unmarshal attempt, so they are preserved as-is.This also keeps
errors.As(err, &apiErr)working for downstream consumers that only handle*openai.Error.Testing
Added
apierror_body_test.go(handwritten, so it doesn't affect the custom-code budget) covering:errorvalue →*openai.Errorwith status code and raw body inMessage