# Codex Token Exchange Failed 403: Diagnose and Recover Sign-In

> A Codex token-exchange 403 is a refused authentication request. The full error tells you whether to repair callback access, check the client’s network, renew stored credentials, or stop for an access restriction.

- URL: https://blog.laozhang.ai/en/posts/codex-token-exchange-failed-403
- Published: 2026-07-12
- Updated: 2026-10-06
- Author: LaoZhang AI Team (https://blog.laozhang.ai/en/about)
- Topic: Developer Tools & Agents
- Tags: OpenAI Codex, Codex CLI, Token Exchange, 403 Forbidden, OAuth, Troubleshooting

---
`Token exchange failed: token endpoint returned status 403 Forbidden` means the code-for-token request received a refusal. **Read the text after `403` before changing anything.** An explicit country, account, or workspace restriction needs an eligibility or administrator check. A request-sending error needs a network check in the environment running Codex. A browser that finishes while the terminal keeps waiting needs a callback check. These lead to different recovery steps.

Run `codex --version` and `codex login status` in the shell or host that failed, keep the redacted error, then use the matching branch below. A successful status command only confirms that credentials are present; it does not prove they work remotely. The [CLI reference](https://learn.chatgpt.com/docs/developer-commands#codex-login) makes that distinction.

![Codex token-exchange 403: read the full error, preserve response details, distinguish an unexplained refusal from a country, account or workspace restriction, and check the actual Codex host](https://blog.laozhang.ai/posts/en/codex-token-exchange-failed-403/img/cover.webp)

## Match the full error to the next action

| Error or symptom | What it establishes | Next action |
| --- | --- | --- |
| `token endpoint returned status 403 Forbidden`, with no further explanation | The exchange request received a refusal; the cause remains unknown | Preserve the response details, then check the actual Codex process’s network and login configuration |
| `unsupported_country_region_territory` or an explicit country/region denial | The response identifies an access restriction | Check the current official eligibility information; contact support if it appears wrong. Stop proxy and cache experiments |
| `error sending request for url` followed by the token endpoint | The client could not complete that request; the nested error matters | Check the failing host’s outbound route, proxy and certificate trust. Do not treat it as identical to an HTTP 403 response |
| Browser approval completes, but Codex keeps waiting for the callback | The browser result has not reached the waiting process | Use an enabled device-code flow; otherwise arrange callback access with the remote-host owner |
| Device-code request returns 403 | That device-code request was refused | Read its body and confirm device login is enabled and permitted; switching flows does not override a country or policy denial |
| Login finishes, then a task fails with 401, 403, or a refresh/workspace message | The failure is after the initial exchange | Check the active method, workspace and provider route; preserve the new error as a separate failure |

The [HTTP definition of 403](https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.4) allows refusal for reasons unrelated to credentials. It also advises against automatically repeating the same request with the same credentials. The number alone does not establish that OpenAI’s origin handled the request: a proxy or another intermediary can return a refusal too.

### If the response names a country or region, stop here

For a ChatGPT sign-in rejection, open the current [ChatGPT supported-country information](https://help.openai.com/en/articles/7947663-chatgpt-supported-countries) and compare it with your actual situation. This guide does not certify any particular country or account as currently eligible. If you believe the refusal is mistaken, use [OpenAI Support](https://help.openai.com/) with the exact redacted error, timestamp and request ID, if one was returned.

A paid subscription, a new browser session, device-code login or a different credential store does not resolve an explicit eligibility refusal. Do not use another person’s account or a network workaround to evade it.

## Start in the same environment that failed

For CLI sign-in, record these before a retry:

```bash
codex --version
codex login status
```

Write down whether that process runs on your computer, in WSL, in a container, on an SSH host, or inside an IDE’s remote environment. Browser access on your laptop does not demonstrate outbound access from a remote host. Likewise, variables exported in a new terminal do not establish what an already-running IDE inherited.

Keep the exact error text, timestamp with time zone, client version, authentication method and a short redacted response body. Direct `codex login` runs write `codex-login.log` under the configured log directory, according to the [authentication guide](https://learn.chatgpt.com/docs/auth#login-diagnostics). Inspect it privately for the first relevant failure, rather than publishing the entire file.

Remove authorization codes, device codes, tokens, cookies, full callback URLs, API keys, proxy passwords and unrelated private paths from anything you share. If the body is an HTML block page with a company or proxy identifier, preserve that identifier without the secrets.

You can also check the [live OpenAI status page](https://status.openai.com/). An incident may change when to retry; the absence of a listed incident does not determine the cause of your individual refusal.

## Browser approved, but token exchange failed

![Codex authentication stages from browser authorization and callback to token exchange, credential storage, and a signed-in request](https://blog.laozhang.ai/posts/en/codex-token-exchange-failed-403/img/auth-stages.webp)

Browser approval, callback delivery, token exchange, credential storage and a working task are separate milestones. A browser success message confirms only its part of the flow. The exact token-endpoint 403 points to the subsequent exchange attempt, so forwarding a callback port alone may leave it unchanged.

First check the network used by the Codex process. If your organization requires an outbound proxy, use its approved address and protocol. If its block page names a denied authentication destination, give the network owner that hostname and the redacted block identifier. Changing the model API Base URL is a different operation and does not establish a repair to the sign-in endpoint.

Do not rely on the blanket advice that Codex always ignores system proxies. The current official [changelog](https://learn.chatgpt.com/docs/changelog) records system-proxy fallback for login and startup requests. That entry does not promise that fallback will repair every 403 or describe all platform and retry conditions. Check your installed client and actual launcher instead of assuming another user’s setup applies.

### Check proxy presence without printing proxy credentials

For a POSIX shell, the following reports only whether each variable is set. It deliberately does not print values; “set” can include an empty value.

```bash
for name in HTTPS_PROXY HTTP_PROXY ALL_PROXY NO_PROXY https_proxy http_proxy all_proxy no_proxy; do
  if printenv "$name" >/dev/null 2>&1; then
    printf '%s: set\n' "$name"
  else
    printf '%s: unset\n' "$name"
  fi
done
```

Run it in the shell from which you will launch Codex. It checks that shell, not a separate GUI process, system proxy settings or whether any proxy works. On Windows without a POSIX shell, inspect the approved environment settings for the launcher privately; do not paste an environment dump into a ticket.

If the launcher has missing or incorrect proxy settings, correct it to use the approved proxy’s actual address and protocol. Check those values privately with the network owner; there is no universal proxy port or scheme to copy. In a container or remote host, the proxy must be reachable from that environment, not merely from your laptop.

Keep the callback’s local traffic separate from outbound authentication traffic. Preserve any required `NO_PROXY` exclusions; do not add all OpenAI domains to that variable, because your network may require those requests to use its proxy. If the affected application needs to inherit changed settings, restart it through its approved launcher. Then retry from the same environment:

```bash
codex login
```

Compare the new error with the saved one. If the same proxy block remains, send its identifier and the token endpoint hostname to the network owner. If the reply now explicitly names country or workspace policy, stop network edits and use that branch instead.

The presence-only snippet above received offline syntax and fixture checks. These commands are documentation-based recovery examples; no live sign-in or proxy recovery was tested for this article.

### Use a corporate CA only for a certificate-trust failure

If the nested error identifies an untrusted certificate chain and your company uses TLS inspection, obtain its approved PEM bundle. In the same POSIX shell:

```bash
export CODEX_CA_CERTIFICATE="/path/to/approved-company-ca.pem"
codex login
```

The [custom CA documentation](https://learn.chatgpt.com/docs/auth#custom-ca-bundles) says Codex uses `CODEX_CA_CERTIFICATE`, falling back to `SSL_CERT_FILE` when it is unset. This applies to login, HTTPS and secure WebSocket connections. Keep certificate verification enabled. An ordinary HTTP 403 response is not evidence that a new CA bundle is needed.

## Remote login: repair callback reachability

If the browser completes authorization and the remote terminal still waits, the callback may be going to your laptop while its listener is on the remote host. OpenAI documents device-code login and SSH forwarding for this situation in [headless authentication](https://learn.chatgpt.com/docs/auth#login-on-headless-devices).

**Device-code login is the preferred documented option, currently labeled beta.** Enable it in your personal ChatGPT security settings, or have the workspace administrator permit it. Then run on the machine where Codex will work:

```bash
codex login --device-auth
```

Open the link the command prints on your own browser, sign in and enter the one-time code from that terminal. Do not share the code or enter one supplied by someone else. If the device request itself returns 403, return to its response details; this route removes the need for the browser to reach Codex’s callback listener. It still needs outbound connectivity and an eligible account.

If device login is unavailable, the remote-host owner can arrange permitted callback reachability for the normal browser flow. OpenAI documents SSH forwarding as one option in the headless guide above. That addresses a terminal still waiting for a browser callback; it does not repair an exchange request that already returned 403. Do not expose the callback listener to the public internet.

OpenAI also documents transferring your own file-based login cache to a trusted headless machine. That fallback applies only when a successful login produced an actual `auth.json` file, both machines are under your control, and the transfer is permitted. A keyring-only or ephemeral session has no such file to copy. With custom `CODEX_HOME`, use its real location rather than assuming `~/.codex`. Follow the official transfer instructions linked above; do not copy another person’s credentials or treat a copied cache as permission to access a forbidden workspace.

## Renew stored credentials only when that is the problem

![Codex recovery choices for a waiting callback, a request-sending error, a saved-account or refresh problem, and explicit access restrictions, followed by same-host login and request checks](https://blog.laozhang.ai/posts/en/codex-token-exchange-failed-403/img/safe-recovery.webp)

A credential reset is useful when the intended route is allowed but the saved account is wrong, or a later refresh error asks you to sign in again. It is not the first response to an unexplained exchange 403.

For ordinary stored authentication, the documented reset is:

```bash
codex logout
codex login
codex login status
```

Be prepared to sign in again in the IDE too: the CLI and IDE extension share cached login details. If the process selects workload identity from its environment, Codex rejects `login` and `logout`; have the owner of that identity configuration check it instead. These behaviors are described in the [authentication guide](https://learn.chatgpt.com/docs/auth).

Do not delete the entire `~/.codex` directory. Credential storage is configurable, and that directory can hold configuration, logs and other state. The [documented storage modes](https://learn.chatgpt.com/docs/auth#credential-storage) are:

| Mode | Where credentials live | Why deleting the default file may miss the problem |
| --- | --- | --- |
| `file` | `auth.json` under `CODEX_HOME`, normally `~/.codex` | A custom home changes the path |
| `keyring` | OS credential store | The file is not the active store |
| `auto` | OS store when available, otherwise file | The active store depends on availability |
| `ephemeral` | Current process memory | There is no persistent login file |

Automatic refresh normally handles ChatGPT session renewal during use. When it fails, preserve the exact refresh error before replacing credentials; a refresh failure after login is a different stage from the initial code exchange.

### Managed workspace rejection needs an administrator check

If the error names a workspace restriction, ask the administrator to confirm your membership, permitted login methods and allowed workspace. Current [local authentication requirements](https://learn.chatgpt.com/docs/enterprise/managed-configuration#manage-authentication-locally) can enforce `allowed_login_methods`, `allowed_chatgpt_workspaces`, `cli_auth_credentials_store` and `chatgpt_base_url` before credentials load.

A user’s `forced_login_method` and `forced_chatgpt_workspace_id` must comply with those requirements. No matching workspace makes ChatGPT login unavailable; API authentication remains an option only if permitted. A cached session or user configuration override cannot remove the administrator’s restriction. Stop changing local proxy or credential settings once an explicit policy denial identifies the controlling boundary.

## Would API-key login solve your task?

For an allowed local workflow, an API key can provide a different authentication route. With a key already supplied securely through `OPENAI_API_KEY`, the official command is:

```bash
printenv OPENAI_API_KEY | codex login --with-api-key
codex login status
```

Do not put the key itself in the command line or print it separately. API usage is billed through OpenAI Platform at API rates, rather than included ChatGPT plan usage. Some workspace or cloud features are limited or unavailable, and Codex cloud requires ChatGPT sign-in. Those boundaries come from [OpenAI’s authentication documentation](https://learn.chatgpt.com/docs/auth#sign-in-with-an-api-key); the [API key versus subscription guide](https://blog.laozhang.ai/en/posts/codex-api-key-vs-subscription) explains the billing decision.

A permitted enterprise automation flow may instead use a Codex access token. If the environment already supplies it securely, the documented command is:

```bash
printenv CODEX_ACCESS_TOKEN | codex login --with-access-token
```

This requires the enterprise access-token permission described in the [official guide](https://learn.chatgpt.com/docs/auth#use-codex-access-tokens-for-enterprise-automation). It is not an ordinary Platform API key.

Switching routes can let you complete an appropriate task, but it does not prove the original ChatGPT exchange works. For a custom model provider, check whether its configuration intentionally uses OpenAI authentication or its own `env_key`; [custom-provider setup](https://blog.laozhang.ai/en/posts/codex-config-toml) covers that separate configuration task. Neither a model gateway nor a Base URL change is a universal login-403 fix.

## Verify the recovery on the original host and route

After the corrective action, check three results in order:

1. The login flow finishes without the original exchange error, or the previously saved session remains available without needing a new login.
2. `codex login status` reports the intended authentication method in the same host, container or remote environment. Exit code 0 means credentials are present, as the [CLI reference](https://learn.chatgpt.com/docs/developer-commands#codex-login) states.
3. When you are ready to authorize normal usage, start Codex in that same environment and ask for a tiny non-sensitive reply, such as `Reply with ROUTE_OK only. Do not read files or run commands.` A returned answer demonstrates a working request on that route; it may consume plan usage or incur API charges.

If the small task fails with a different error, retain that new error and investigate its stage. A task working on your laptop does not validate a remote host. API-key success does not validate ChatGPT sign-in. A text reply does not establish every tool, streaming feature or workspace capability your real job needs.

If the same refusal persists after one change supported by the evidence, escalate with the exact redacted error, timestamp, client version, execution environment, intended method, callback result and request ID. Route an explicit network block to the network owner, a workspace policy denial to the administrator, and an unexplained or mistaken account refusal to OpenAI Support. Avoid repeated identical attempts that add no diagnostic information.

## Questions about the token-exchange 403

### Why does the browser say sign-in succeeded while Codex fails?

The browser authorization and Codex’s token request are separate steps. If Codex prints the token-endpoint 403, inspect that request’s response. If the terminal is simply still waiting, check callback reachability instead. Neither browser result proves a working Codex session.

### Does `codex login status` prove the 403 is fixed?

No. Exit code 0 means credentials are present, according to the [CLI reference](https://learn.chatgpt.com/docs/developer-commands#codex-login). Confirm the intended method and, when you authorize usage, a small task on the same host and route. Old cached credentials can make status succeed while a new login fails.

### Can device-code login fix a 403 Forbidden?

It can help when the browser cannot reach Codex’s callback listener. It must first be enabled for the account or workspace, and it still needs a working outbound route. It does not override an explicit country, account or policy refusal. See the [documented headless login conditions](https://learn.chatgpt.com/docs/auth#login-on-headless-devices).

### Should I clear `auth.json` or change my proxy first?

Choose from the error details. A saved-session or refresh problem can justify the documented logout/login reset. A request-sending or proxy block error calls for checking the failing process’s route. An explicit access restriction calls for official eligibility or administrator support. Deleting a file may do nothing when credentials are in a keyring, memory or a custom `CODEX_HOME`.

## Sources

External pages this guide links to, in the order they appear. Last updated 2026-10-06.

- [CLI reference](https://learn.chatgpt.com/docs/developer-commands) (learn.chatgpt.com)
- [HTTP definition of 403](https://www.rfc-editor.org/rfc/rfc9110.html) (rfc-editor.org)
- [ChatGPT supported-country information](https://help.openai.com/en/articles/7947663-chatgpt-supported-countries) (help.openai.com)
- [OpenAI Support](https://help.openai.com/) (help.openai.com)
- [authentication guide](https://learn.chatgpt.com/docs/auth) (learn.chatgpt.com)
- [live OpenAI status page](https://status.openai.com/) (status.openai.com)
- [changelog](https://learn.chatgpt.com/docs/changelog) (learn.chatgpt.com)
- [local authentication requirements](https://learn.chatgpt.com/docs/enterprise/managed-configuration) (learn.chatgpt.com)
