openai/openai-python · error · OpenAIError
X.509 workload identity cannot be changed after client const
Error message
X.509 workload identity cannot be changed after client construction
What it means
The X.509 auth handler snapshots the workload identity at client construction. If `client.workload_identity` is later mutated/replaced so it no longer equals the captured identity, `_send_with_auth_retry` refuses the request because cert rotation outside the client could break mTLS mid-flight.
Source
Thrown at src/openai/_client.py:537
def qs(self) -> Querystring:
return Querystring(array_format="brackets")
def _send_with_auth_retry(
self,
request: httpx2.Request,
*,
stream: bool,
retried: bool = False,
**kwargs: Unpack[HttpxSendArgs],
) -> httpx2.Response:
used_access_token: str | None = None
request_is_replayable = False
x509_auth = self._workload_identity_auth
if x509_auth is not None:
if isinstance(x509_auth, SyncX509WorkloadIdentityAuth):
if x509_auth.workload_identity != self.workload_identity:
raise OpenAIError("X.509 workload identity cannot be changed after client construction")
validate_x509_api_url(request.url, expected_origin=self.base_url)
validate_x509_request_authority(request)
validate_x509_api_credentials(request)
if x509_auth._follow_redirects is not None:
kwargs["follow_redirects"] = x509_auth._follow_redirects
authorization = request.headers.get("Authorization")
if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}":
used_access_token = (
x509_auth.get_token_for_request(request)
if isinstance(x509_auth, SyncX509WorkloadIdentityAuth)
else x509_auth.get_token()
)
request.headers["Authorization"] = f"Bearer {used_access_token}"
request_is_replayable = x509_auth._can_retry_request(request)
if isinstance(x509_auth, SyncX509WorkloadIdentityAuth):
response = x509_auth.send_api_request(
request,View on GitHub (pinned to 9917c6e28e)
Solutions
- Build a new client when rotating: `client = OpenAI(workload_identity=new_identity)`
- Remove any code that writes `client.workload_identity` post-construction
- Scope one client per identity in multi-tenant setups
Example fix
# before client.workload_identity = rotated_identity resp = client.models.list() # after client = OpenAI(workload_identity=rotated_identity) resp = client.models.list()
Defensive patterns
Strategy: validation
Validate before calling
# treat clients as immutable; on rotation rebuild
if new_identity is not client.workload_identity:
client = OpenAI(workload_identity=new_identity, base_url=client.base_url) Try / catch
try:
resp = client.models.list()
except OpenAIError as e:
if 'cannot be changed after client construction' in str(e):
client = OpenAI(workload_identity=client.workload_identity) # rebuild
resp = client.models.list()
else:
raise Prevention
- Never assign to client.workload_identity after construction
- Structure cert rotation as: build new client, then atomically swap the reference
- One client per identity/tenant
When it happens
Trigger: Assigning `client.workload_identity = new_identity` after construction; mutating the identity dict in place between requests; sharing one client across rotation code that swaps identities.
Common situations: Certificate-rotation logic that updates the client attribute instead of rebuilding the client; test harnesses mutating state; multi-tenant code trying to reuse one client with different identities.
Related errors
- Invalid `workload_identity` configuration: expected an X.509
- The `api_key` and `workload_identity` arguments are mutually
- The token file at {token_file_path} is empty.
- Failed to read the token file at {token_file_path}: {e}
- Missing credentials. Please pass an `api_key`, `workload_ide
AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28).
Data as JSON: /api/errors/65eea32e0f851a92.
Report an issue: GitHub.