Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/openai/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ def __init__(self, message: str, request: httpx2.Request, *, body: object | None
self.body = body

if is_dict(body):
self.code = cast(Any, construct_type(type_=Optional[str], value=body.get("code")))
raw_code = body.get("code")
self.code = str(raw_code) if raw_code is not None else None
self.param = cast(Any, construct_type(type_=Optional[str], value=body.get("param")))
self.type = cast(Any, construct_type(type_=str, value=body.get("type")))
else:
Expand Down
7 changes: 5 additions & 2 deletions src/openai/auth/_workload.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import math
import time
import threading
from typing import Any, Generic, TypeVar, Callable, TypedDict, cast
Expand Down Expand Up @@ -305,8 +306,10 @@ def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]:
)

def _validate_expires_in(self, expires_in: object) -> float:
if not isinstance(expires_in, (int, float)):
raise OpenAIError("Token exchange response did not include a valid expires_in")
if isinstance(expires_in, bool):
expires_in = int(expires_in)
Comment on lines +309 to +310

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject boolean expiry values instead of coercing them

For a malformed JSON response containing "expires_in": true, these lines turn the boolean into 1.0, cache the token, and proceed with the API request even though booleans are not numeric expiration values; the X.509 validator explicitly rejects the same input. Return a validation error for both booleans and add focused sync/async coverage.

AGENTS.md reference: AGENTS.md:L41-L45

Useful? React with 👍 / 👎.

if not isinstance(expires_in, (int, float)) or math.isnan(expires_in) or math.isinf(expires_in) or expires_in <= 0:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle oversized integers before calling math predicates

If a token response contains an oversized integer such as 10**400, math.isnan(expires_in) raises OverflowError while converting it to a float, so the intended finite-value rejection is never reached and the public request path retries and misreports it as a connection failure. Convert inside an OverflowError guard, as _as_finite_float already does for X.509 authentication, and test both sync and async flows.

AGENTS.md reference: AGENTS.md:L41-L45

Useful? React with 👍 / 👎.

raise ValueError("Token exchange response did not include a valid expires_in")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve OpenAIError for invalid expiry responses

When the token endpoint returns a newly rejected value such as 0, -1, NaN, or infinity, this now raises ValueError; both sync and async request loops only propagate OpenAIError directly, so they instead retry the token exchange and eventually wrap the validation failure as APIConnectionError. Raise OpenAIError here as the previous validation did, and cover both client paths.

AGENTS.md reference: AGENTS.md:L41-L45

Useful? React with 👍 / 👎.

return float(expires_in)

def _token_unusable(self) -> bool:
Expand Down