Skip to content

fix: allow api_key="" to bypass credential validation for local servers - #3225

Open
tbille wants to merge 3 commits into
openai:mainfrom
tbille:fix/empty-api-key-credential-check
Open

fix: allow api_key="" to bypass credential validation for local servers#3225
tbille wants to merge 3 commits into
openai:mainfrom
tbille:fix/empty-api-key-credential-check

Conversation

@tbille

@tbille tbille commented May 11, 2026

Copy link
Copy Markdown

Summary

Fixes #3224

  • In v2.34.0, the credential validation in _client.py changed from an identity check (api_key is None) to a truthiness check (not self.api_key), which causes api_key="" to be rejected as "Missing credentials"
  • This broke OpenAI-compatible local servers (llama.cpp, llamafile, LM Studio, vLLM, etc.) that don't require authentication and intentionally pass api_key=""
  • This fix tracks whether api_key was explicitly provided by the caller and skips the credential error when it was, even if the value is an empty string

Changes

src/openai/_client.py: Added _api_key_explicitly_set local variable in both OpenAI.__init__() and AsyncOpenAI.__init__() that tracks whether the caller explicitly passed an api_key argument (vs it being resolved from the environment or defaulting to None). The credential validation check now also considers this flag, so api_key="" no longer triggers the error while api_key=None with no env var still does.

tests/test_client.py: Added test cases for both sync and async clients verifying that api_key="" does not raise OpenAIError, restoring the pre-v2.34.0 behavior.

@tbille
tbille requested a review from a team as a code owner May 11, 2026 19:32

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 168a416518

ℹ️ 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".

Comment thread src/openai/_client.py Outdated
@tbille

tbille commented May 26, 2026

Copy link
Copy Markdown
Author

can we get an update on this issue?

@tbille
tbille force-pushed the fix/empty-api-key-credential-check branch 2 times, most recently from 397125f to f96258a Compare June 2, 2026 12:40
@tbille
tbille force-pushed the fix/empty-api-key-credential-check branch from f96258a to e1e32dd Compare June 15, 2026 07:31
@tbille
tbille force-pushed the fix/empty-api-key-credential-check branch from e1e32dd to 5d0d0ad Compare June 22, 2026 19:56
tbille added 2 commits August 5, 2026 11:35
In v2.34.0, the credential validation changed from an identity check
(api_key is None) to a truthiness check (not self.api_key), which
caused api_key="" to be rejected as missing credentials. This broke
OpenAI-compatible local servers (llama.cpp, llamafile, LM Studio, vLLM)
that don't require authentication.

Track whether api_key was explicitly provided by the caller and skip
the credential error when it was, even if the value is an empty string.

Fixes openai#3224
Move _api_key_explicitly_set check before the OPENAI_API_KEY env var
lookup so that only caller-provided api_key="" bypasses validation.
OPENAI_API_KEY="" in the environment (likely misconfiguration) still
raises the Missing credentials error.

Add tests for the OPENAI_API_KEY="" env var scenario.
@tbille
tbille force-pushed the fix/empty-api-key-credential-check branch from 5d0d0ad to c8c6694 Compare August 5, 2026 09:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8c66949e1

ℹ️ 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".

Comment thread src/openai/_client.py
provider_runtime is None
and _enforce_credentials
and not self.api_key
and not _api_key_explicitly_set

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 Carry the explicit empty api_key through request auth

When a caller explicitly passes api_key="" for an auth-less local server, this constructor check now allows the client to be created, but the first normal bearer-auth request still fails: _bearer_auth/auth_headers return no Authorization header for an empty key, and _build_headers() then calls _validate_headers(), which raises because no auth header was resolved. The same pattern exists in AsyncOpenAI, so the new path only passes construction tests and still cannot make typical SDK calls unless users manually omit or provide an Authorization header.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — confirmed the empty api_key was still rejected at request time. Fixed in 3214527: added _api_key_explicitly_empty tracking (limited to a literal explicitly-passed api_key="", not key-providers or workload identity) and skip the auth-header validation for that case in _validate_headers, unless the request specifically requires admin_api_key_auth (which an empty api_key still can't satisfy). Had to thread security into _validate_headers (and update the AzureOpenAI overrides) to make that distinction precisely. Added regression tests that build actual requests (not just construct the client) for both the bearer_auth-succeeds and admin_api_key_auth-still-raises cases.

Client construction with api_key="" was fixed to not raise "Missing
credentials", but _validate_headers still rejected the first actual
request for bearer_auth endpoints since no Authorization header could
be built from an empty key. This left the local-server use case
(llama.cpp, LM Studio, vLLM, etc.) broken end-to-end.

Track whether the caller passed a literal empty string (as opposed to
a key provider, workload identity, or unset value) and skip the
'Could not resolve authentication method' error for such clients,
except when the request specifically requires admin credentials that
an empty api_key cannot satisfy. Thread the request's security
requirements into _validate_headers to make that distinction, updating
the AzureOpenAI overrides to match the new signature.

Addresses review feedback from PR openai#3225.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3214527305

ℹ️ 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".

Comment thread src/openai/_client.py
# don't fail requests just because no `Authorization` header could be built —
# unless the request specifically requires admin credentials, which an empty
# `api_key` cannot satisfy.
if self._api_key_explicitly_empty and not (security or {}).get("admin_api_key_auth", False):

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 Allow empty api_key on raw client requests

When api_key="" is used with the public raw helpers (client.get("/foo", ...), post, etc.) and no options["security"] override is supplied, FinalRequestOptions still has its default admin_api_key_auth=True. This check therefore treats the request as admin-only and raises before it reaches an auth-less local/custom endpoint, even though generated endpoints pass security={"bearer_auth": True}. The fresh evidence beyond the earlier auth-header finding is that the raw helper path constructs options without a security override, so the default admin flag still blocks the new empty-key mode.

Useful? React with 👍 / 👎.

Comment thread src/openai/_client.py
# This is needed so that requests don't fail header validation below when the
# caller intentionally disabled authentication (e.g. for local, auth-less
# OpenAI-compatible servers).
self._api_key_explicitly_empty = api_key == ""

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 empty api_key outside direct construction

This new explicit-empty state is only captured when the constructor receives api_key="", but other supported configuration paths do not preserve that intent: copy()/with_options() still uses api_key or self._api_key_provider or self.api_key, so client.with_options(api_key="", base_url=local_url) inherits and sends the previous non-empty key instead of disabling auth, and an already-loaded module client whose openai.api_key is later set to "" never updates this flag and still fails bearer-auth request validation. Please thread the explicit-empty state through these reconfiguration paths so local auth-less servers work consistently.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AsyncOpenAI(api_key="") raises OpenAIError in v2.34.0, breaking OpenAI-compatible local servers

1 participant