Troubleshooting
For questions and feedback, use Claude Code support, or open an issue on the Claude Code GitHub repository. When reporting a problem, include:
- Gateway issue: the gateway's stderr for the relevant window, your
gateway.yamlwith secrets redacted, the gateway version, shown on the landing page at/and in thex-cc-gateway-versionresponse header on/managed/settings, and what changed recently - Login issue: the developer runs
claude --debug-file ./claude-debug.txt, reproduces, and sends that file plus the gateway's audit log for the same window - Inference issue: the model requested, the upstreams configured, and the gateway's audit log for the request, which records which upstream served it and the response status
The gateway's stderr includes the audit event stream, the audit log records developer identities, and the debug file records hook and MCP server output from the developer's machine. Review and redact these before posting to a public issue.
| Symptom | Cause | Fix |
|---|---|---|
A developer's /login shows the standard account picker instead of the Cloud gateway screen | forceLoginMethod or forceLoginGatewayUrl isn't set in managed settings on that machine | Deploy the managed settings file to the device; /login reads the gateway URL from there |
A developer's requests fail with Not signed in to the Cloud gateway — run /login. | The machine's managed settings set forceLoginMethod: "gateway" or forceLoginGatewayUrl, and the session has no gateway sign-in. A leftover claude.ai login doesn't satisfy the requirement. | Have the developer run /login and complete the gateway sign-in. See also Administrator policy requires a Cloud gateway sign-in. |
| Claude Desktop reports that its bootstrap configuration couldn't be fetched | /user/bootstrap returned 404: the policy matching the user doesn't carry a desktop key, or no policy matched. The gateway's audit log records each rejection as desktop_bootstrap.denied with the reason. | Add a desktop block to the policy that matches the user, or to the match: \{\} base layer; an empty desktop: \{\} suffices. See Claude Desktop overlay. |
Startup shows Gateway login is configured in managed settings, but this Claude Code build does not include Cloud gateway support. | The installed Claude Code build predates gateway support | Have the developer update Claude Code to a release that includes Cloud gateway support |
Startup or /login reports Claude Code may not be enabled for your organization after a 403 on the managed settings load | The gateway, or something in front of it, answered the /managed/settings request with 403. The gateway's own settings route never answers 403. The status comes from the access_control IP checks or from a proxy or WAF in front of the gateway. The audit log records an IP-check denial as access.denied with the reason. The developer stays signed in. | Check the audit log for access.denied at the time of the failure and fix the access_control lists or the front end, then have the developer start claude again |
CLI /login: Gateway hosts must be on your organization's private network; <host> resolves to the public (or unrecognized) address <ip> | The gateway hostname resolves to at least one public IP address. Claude Code checks each resolved address and requires every one to be private. A common cause is a dual-stack name where one family resolves to a public address, including AWS internal dual-stack load balancers, which return public-range AAAA addresses. | Have the gateway name resolve only to private addresses on developer machines. For a dual-stack name, drop the public-range record or serve a separate internal-only DNS name. See the private-network prerequisite. |
CLI /login: Gateway login would go through proxy <proxy>, which is not on a private network | An HTTPS_PROXY or HTTP_PROXY applies to the gateway host and the proxy's hostname resolves to a public address. A proxy whose host resolves only to private addresses is allowed and doesn't trigger this error | Add the gateway host to NO_PROXY on the developer's machine so the connection is direct, or use a proxy whose hostname resolves to private addresses. The message names the exact NO_PROXY entry to add |
CLI /login: Could not resolve the configured HTTP proxy | The hostname in HTTPS_PROXY or HTTP_PROXY doesn't resolve from the developer's machine, typically because it isn't connected to the corporate network | Have the developer connect to your network or VPN and retry, or fix the proxy URL |
CLI /login: Could not resolve gateway host <host> | The machine can't resolve the gateway's internal DNS name, typically because it isn't on the corporate network | Have the developer connect to your network or VPN, then retry /login |
Boot exits with a config validation error naming store.postgres_url | No Postgres configured; the gateway requires Postgres | Set store.postgres_url. For local development, use a throwaway container: docker run --rm -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres. |
Boot exits: requires the native binary | Running under Node instead of the native binary | Install Claude Code with one of the standalone install methods |
Boot exits with an OIDC discovery error after config.load | oidc.issuer unreachable, or TLS chain not trusted | Check the issuer is reachable from the pod and serves /.well-known/openid-configuration. Set ca_cert_pem for private PKI. If the pod reaches the IdP only through a forward proxy, set oidc.use_proxy: true; on versions before v2.1.227, give the pod a direct route to each of the IdP's endpoints instead. |
| Boot exits with a Postgres permission error | The database role lacks DDL rights on its schema | Grant the role CREATE on the gateway's schema so it can create and alter its tables at boot |
/oauth/callback shows "Sign-in could not be completed" | Email domain rejected, id_token validation failed, or email_verified is explicitly false, which the gateway always rejects with no override | Check allowed_email_domains and that the IdP returns a verified email claim. For email_verified: false, fix the IdP-side verification. If your IdP emits email under a different claim name, set oidc.email_claim. |
Log: token exchange failed request_id=<id>: id_token missing email claim | The IdP isn't including email in the id_token by default. This rejection fires only when allowed_email_domains is set; without it, a missing email mints a session with no email | Configure the IdP to emit email in the id_token. Okta: add email to a custom authorization server's ID-token claims. Entra: add email as an optional claim on the app registration. PingFederate: enable an OpenID Connect Policy that emits email. If the IdP serves email from the userinfo endpoint but won't include it in the id_token, such as the Okta org authorization server, set oidc.userinfo_fallback: true. |
Log: refresh failed request_id=<id>: invalid_token (…) (at userinfo_no_id_token, …), and developers see Cloud gateway session expired every session.ttl_hours | The IdP accepted the refresh token but returned no id_token with it, so the gateway asked the IdP's userinfo endpoint for the user's claims. The IdP rejected the refreshed access token there. The gateway answers temporarily_unavailable, so Claude Code keeps the refresh token but can't renew the session. Gateway versions before v2.1.260 log the same line without the (at …) detail. | Set oidc.scope_on_refresh: true, available in gateway v2.1.260 or later, so the refresh request asks for openid again. Some IdPs, such as Okta, return an id_token on refresh only when asked. On PingFederate, enable Return ID Token On Refresh Grant under Applications > OAuth > OpenID Connect Policy Management instead. The key doesn't change PingFederate's behavior. For other IdPs that still omit it, check whether the userinfo endpoint accepts access tokens issued by a refresh. As a stopgap, raise session.ttl_hours. See Identity provider setup for the deprovisioning tradeoff. |
Every Amazon Bedrock request returns 502; log shows Could not load credentials from any providers | On EC2, IMDSv2's default hop limit of 1 blocks the instance-metadata request from inside the container. Boot and /readyz pass anyway because the AWS SDK resolves instance credentials on the first request, not at client construction | Raise the hop limit with aws ec2 modify-instance-metadata-options --instance-id <id> --http-put-response-hop-limit 2, or set it in the launch template. The change applies to every container on the instance. Prefer ECS task roles where available, which read credentials from the ECS container-credentials endpoint and avoid the change entirely, or apply the change on a dedicated gateway instance to limit the exposure. |
| IdP error: unknown or unsupported scope | The IdP rejects scopes it doesn't recognize | Set oidc.scopes to exactly the list your IdP accepts; it must include openid. The default is openid profile email offline_access. |
Sessions don't silently renew after setting oidc.scopes | offline_access was dropped from the override | Add offline_access back if your IdP supports it. Without a refresh token, developers re-run the browser login every session.ttl_hours. |
| Browser shows "This request came from another site and was blocked" | Cross-site form POST, blocked as CSRF protection. Expected for embedded or proxied pages | Open the verification link directly |
| Chrome blocks the Approve button with "Refused to send form data … violates … Content Security Policy directive: form-action", but the same page works in Safari or Firefox | Chrome enforces form-action against the entire redirect chain. Your IdP redirects onward to a second host that isn't allowlisted. | Add each additional origin in the redirect chain to oidc.form_action_origins. Open Chrome DevTools → Console on the Approve page to see which origin was blocked. |
| Sign-in completes at the IdP but the callback fails, with a CSP error in Chrome or "this sign-in link has expired" in Safari | The IdP returned the code via response_mode=form_post, which auto-submits it cross-origin via POST to /oauth/callback. Chrome blocks that under a strict CSP; Safari allows the submit but the callback reads only the query string. | Make sure your IdP honors response_mode=query, which the gateway requests explicitly so the callback is a plain redirect |
| Login works locally but fails behind an ALB | public_url still names the local or inner http:// origin, so the IdP gets the wrong redirect_uri | Set listen.public_url to the external https:// origin and register <public_url>/oauth/callback with the IdP |
| Developer sees the trust prompt repeatedly | TLS cert is rotating per replica or per request | Use a stable cert at the ingress, or terminate TLS once and run replicas over plain HTTP internally |
CLI /login: "Could not verify the gateway's TLS certificate" or SELF_SIGNED_CERT_IN_CHAIN | Gateway's TLS chain is signed by a private CA not in the CLI host's trust store | Claude Code reads the OS trust store by default on the native binary and on Node 22.15 or later; CLAUDE_CODE_CERT_STORE controls this behavior. If the CA is installed in the OS trust store, ensure developers are on a current runtime. Otherwise set NODE_EXTRA_CA_CERTS to the CA certificate PEM before launching. The first-connect fingerprint prompt still applies. |
CLI /login completes the browser sign-in, then the session ends with Cloud gateway sign-in was not completed and a TLS certificate mismatch | On the first request after sign-in, the gateway presented a certificate that doesn't match the fingerprint Claude Code pinned, so Claude Code kept no gateway credential. The usual causes are replicas behind one address that serve different certificates, or something on the network path that intercepts TLS. | Serve one certificate for the hostname, for example by terminating TLS once at the ingress, then have the developer run /login again. If that certificate differs from the pinned one, Claude Code shows the trust prompt again with a warning that the certificate changed. |
CLI /login stops with The gateway's TLS certificate changed during sign-in: it no longer matches the one you trusted | A sign-in request reached a server whose certificate doesn't match the one the developer accepted when /login started: replicas behind one address serving different certificates, TLS interception on the path, or a certificate rotation while the sign-in was in progress. | Serve one certificate for the hostname, then have the developer start the sign-in again and review the new certificate at the trust prompt. |
The Cloud gateway sign-in was not completed message names the gateway hostname. When Claude Code has both the pinned fingerprint and the presented one, the message also shows the first 16 characters of each.
If Claude Code reports couldn't load your organization's managed settings after a gateway sign-in, Claude Code names the reason, restarts in place, and resumes the conversation. If Claude Code can't restart, for example in a background session, Claude Code ends the session and keeps the sign-in.