fix(exceptions): coerce APIError.code to str to match Optional[str] annotation - #3781
Open
okxint wants to merge 2 commits into
Open
fix(exceptions): coerce APIError.code to str to match Optional[str] annotation#3781okxint wants to merge 2 commits into
okxint wants to merge 2 commits into
Conversation
…nnotation
APIError.code is annotated Optional[str], but the OpenAI API (and
compatible gateways) can return a JSON number in the error.code field.
construct_type returns non-matching values unchanged, and the cast(Any,…)
suppressed type-checker warnings, so integer codes silently escaped as
int at runtime — crashing downstream code that trusted the annotation
(e.g. exc.code.strip() raises AttributeError).
Fix: replace the construct_type call with an explicit coercion:
raw_code = body.get("code")
self.code = str(raw_code) if raw_code is not None else None
This keeps the annotation correct (Optional[str]), is backward-compatible
for callers that already receive string codes, and converts integer codes
(404, 429, etc.) to their string representations.
Fixes openai#3531
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dajiaohuang
added a commit
to dajiaohuang/openai-python
that referenced
this pull request
Sep 2, 2026
…nnotation Replace construct_type call with explicit str coercion to properly handle numeric code values returned by the API. Fixes openai#3781
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
This is the same runtime fix already open in #3611, but this version drops the regression that proves numeric codes are normalised. I'd either close this in favour of #3611 or carry over a focused int/string/None test. Otherwise the exact construct_type mismatch that motivated the change isn't pinned by CI.
Author
|
Thanks for the review @sylvesterkaczmarek. Added a focused regression in the latest commit that pins int→str, str→str, and None→None coercion under CI. Happy to close in favor of #3611 if the maintainers prefer — but since #3611 still shows a formatting failure I figured it's worth keeping this one moving. |
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
APIError.codeis typedOptional[str]but could silently hold anintat runtime when the API returns a numeric error code.Root cause:
construct_typereturns the raw value unchanged when the runtime type doesn't match the target type. Wrapping withcast(Any, ...)suppresses the type checker, so a numericcode(e.g.1001) escapes into.codeas anint. Any downstream code that treats.codeas a string (.strip(),.startswith(), string comparison) will raiseAttributeError: 'int' object has no attribute 'strip'.Fix:
Explicit
str()coercion guarantees the annotation is always honoured regardless of what the API sends.Checklist
Optional[str]annotation onAPIError.codeNoneis preserved whencodekey is absent ornullcast(Any, ...)type-suppression workaround