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

  1. Build a new client when rotating: `client = OpenAI(workload_identity=new_identity)`
  2. Remove any code that writes `client.workload_identity` post-construction
  3. 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

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


AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28). Data as JSON: /api/errors/65eea32e0f851a92. Report an issue: GitHub.