openai/openai-python · error · ValueError
Unable to handle auth
Error message
Unable to handle auth
What it means
An internal invariant in _prepare_options: the request had neither an api-key header, nor an api-key-bearing client, nor an Authorization header. The comment says it 'should never be hit', so hitting it indicates corrupted client state or a custom subclass interfering with auth header generation.
Solutions
- Avoid mutating default_headers to strip Authorization/api-key; construct a new client instead
- If subclassing, ensure _auth_headers/_prepare_options overrides preserve the contract
- Report as a bug if reproducible with the unmodified client
Defensive patterns
Strategy: try-catch
Try / catch
try:
client.chat.completions.create(...)
except ValueError as e:
if str(e) == "Unable to handle auth":
rebuild_client_with_fresh_auth()
raise Prevention
- Don't strip auth headers at runtime
- Avoid overriding internal auth methods in subclasses
When it happens
Trigger: Mutating client.auth/default_headers after construction to remove auth, subclassing AzureOpenAI and overriding _prepare_options/_auth_headers incorrectly, or a custom api_key provider returning a value that bypasses the api-key branch.
Common situations: Essentially unreachable in normal use; appears only with subclasses or direct header manipulation.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Azure IMDS response did not include an access_token
- base_url and azure_endpoint are mutually exclusive
- Configure `provider` on `AsyncOpenAI`, not on…
- Configure `provider` on `OpenAI`, not on…
- "Could not resolve authentication method. Expected either…
AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28).
Data as JSON: /api/errors/3e153c2c3218c8ec.
Report an issue: GitHub.
Appendix: source
Thrown at src/openai/lib/azure.py:496
self._refresh_api_key()
headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {}
options = model_copy(options)
options.headers = headers
azure_ad_token = self._get_azure_ad_token()
if azure_ad_token is not None:
if not _has_header(headers, "Authorization"):
headers["Authorization"] = f"Bearer {azure_ad_token}"
elif self.api_key and self.api_key != API_KEY_SENTINEL:
if not _has_header(headers, "api-key"):
headers["api-key"] = self.api_key
elif _has_auth_header(headers) or _has_auth_header(self.default_headers):
pass
else:
# should never be hit
raise ValueError("Unable to handle auth")
return options
def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx2.URL, dict[str, str]]:
auth_headers = {}
query = {
**extra_query,
"api-version": self._api_version,
"deployment": self._azure_deployment or model,
}
if self.api_key and self.api_key != "<missing API key>":
auth_headers = {"api-key": self.api_key}
else:
token = self._get_azure_ad_token()
if token:
auth_headers = {"Authorization": f"Bearer {token}"}
if self.websocket_base_url is not None:View on GitHub (pinned to 9917c6e28e)