[BUG] OAuth login/refresh fails with UNABLE_TO_GET_ISSUER_CERT on platform.claude.com's new Let's Encrypt (ISRG Root X2 cross-signed) chain
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On a headless Linux server, claude login fails with:
OAuth error: SSL certificate error (UNABLE_TO_GET_ISSUER_CERT). If you are behind a
corporate proxy or TLS-intercepting firewall, set NODE_EXTRA_CA_CERTS to your CA bundle
path, or ask IT to allowlist *.anthropic.com. Run /doctor for details.
The error message is misleading: there is no proxy and no TLS interception. The actual cause is that Claude Code's bundled runtime cannot build a valid TLS chain to platform.claude.com after Anthropic rotated that host onto Let's Encrypt's new 2026 hierarchy (leaf → Let's Encrypt YE1 → ISRG Root YE → ISRG Root X2 cross-signed by ISRG Root X1) on 2026-06-26.
The chain is valid — openssl, the system Node runtime, browsers, and fetch() all verify it (Verify return code: 0 (ok)). Only Claude Code's bundled runtime fails. api.anthropic.com uses a Google Trust Services / GlobalSign chain and is unaffected, so normal API traffic works — the failure is isolated to OAuth login and token refresh, which hit platform.claude.com.
Impact
Any user performing a fresh OAuth login or token refresh after the Jun 26 rotation, whose environment relies on Claude Code's bundled root store (minimal/headless/container images, some distros), will be unable to authenticate. API key auth (ANTHROPIC_API_KEY) is unaffected.
Environment
- Claude Code: 2.1.193 — also reproduced on 2.1.181 (not version-specific), installed via pnpm
- OS: Fedora (Linux 7.0.10)
- System Node: nvm v24.18.0 (used only for diagnostics; Claude runs its own bundled runtime)
- No proxy, no MITM, system clock correct
Root cause: two hosts, one runtime, opposite outcomes
| Host | Served chain | Claude runtime |
|------|--------------|----------------|
| api.anthropic.com | GTS WE1 → GTS Root R4 → GlobalSign Root CA (long-established) | OK — reachable (returns 401, i.e. TLS OK) |
| platform.claude.com | LE YE1 → ISRG Root YE → ISRG Root X2 (cross-signed by ISRG Root X1) — issued Jun 26 2026 | FAIL — UNABLE_TO_GET_ISSUER_CERT |
Same runtime, same machine — the only variable is the certificate chain. The new cross-signed ECDSA path is the trigger.
platform.claude.com chain (openssl s_client -showcerts):
0 s:CN=platform.claude.com NotBefore: Jun 26 2026 (reissued)
i:Let's Encrypt, CN=YE1
1 s:Let's Encrypt, CN=YE1
i:ISRG, CN=Root YE
2 s:ISRG, CN=Root YE NotBefore: May 13 2026 (new)
i:ISRG, CN=ISRG Root X2
3 s:ISRG, CN=ISRG Root X2 NotBefore: May 13 2026 (new, cross-signed)
i:ISRG, CN=ISRG Root X1
Verify return code: 0 (ok)
Evidence it is a client-side path-building bug (not Anthropic, not the OS)
All of the following succeed against platform.claude.com / console.anthropic.com using the same machine's trust material:
# openssl, system store
echo | openssl s_client -connect platform.claude.com:443 -servername platform.claude.com 2>/dev/null \
| grep 'Verify return code'
# => Verify return code: 0 (ok)
# system Node https
node -e 'require("https").get("https://api.anthropic.com",r=>console.log("OK",r.statusCode))'
# => OK 404
# system Node global fetch / undici
node -e 'fetch("https://console.anthropic.com/v1/oauth/token",{method:"POST"}).then(r=>console.log("OK",r.status))'
# => OK 415
# explicit ca = system bundle, all hosts
node -e 'const fs=require("fs"),https=require("https");const ca=fs.readFileSync("/etc/pki/tls/cert.pem");
for(const h of ["api.anthropic.com","console.anthropic.com","claude.ai"])
https.get({host:h,path:"/",ca},r=>console.log(h,r.statusCode)).on("error",e=>console.log(h,e.code));'
# => all OK (301/403/404)
Relevant Claude Code debug log lines (--debug):
[DEBUG] CA certs: stores=bundled,system, extraCertsPath=<system bundle>
[DEBUG] CA certs: Loaded 120 bundled root certificates
[DEBUG] CA certs: Loaded 813 system CA certificates
[DEBUG] CA certs: Appended extra certificates from NODE_EXTRA_CA_CERTS
[DEBUG] mTLS: Creating HTTPS agent with custom certificates
...
[ERROR] OAuth refresh failed (expected): unable to get issuer certificate
[ERROR] Failed to fetch oauth profile from OAuth token: ... 401
[ERROR] [Bootstrap] fetchBootstrapData failed: AxiosError: Request failed with status code 401
Note the runtime does load bundled + system + extra certs, yet still cannot complete the platform.claude.com path — strongly suggesting the custom HTTPS agent's path-builder does not use the server-supplied intermediates and/or cannot bridge the new ISRG Root X2 cross-sign to ISRG Root X1.
Workaround
Hand the runtime the full served chain plus the ISRG roots:
echo | openssl s_client -connect platform.claude.com:443 -servername platform.claude.com -showcerts 2>/dev/null \
| awk '/-----BEGIN CERTIFICATE-----/{f=1} f; /-----END CERTIFICATE-----/{f=0}' > ~/lechain.pem
curl -fsSL https://letsencrypt.org/certs/isrgrootx1.pem -o ~/x1.pem
curl -fsSL https://letsencrypt.org/certs/isrg-root-x2.pem -o ~/x2.pem
cat ~/lechain.pem ~/x1.pem ~/x2.pem /etc/pki/tls/cert.pem > ~/claude-ca.pem
export NODE_EXTRA_CA_CERTS=~/claude-ca.pem
claude # logs in successfully
To persist (note: ~ does not expand inside double quotes, so use $HOME):
echo 'export NODE_EXTRA_CA_CERTS="$HOME/claude-ca.pem"' >> ~/.bashrc
/etc/pki/tls/cert.pemis the Fedora/RHEL system bundle path — on Debian/Ubuntu it's/etc/ssl/certs/ca-certificates.crt. Adjust if your distro differs.
Pointing NODE_EXTRA_CA_CERTS at just the system root bundle is not sufficient — the new YE1/ISRG Root YE/X2-cross intermediates must be present locally, because the runtime does not appear to use the server-supplied ones.
What Should Happen?
OAuth login/refresh validates platform.claude.com exactly as openssl, browsers, and stock Node do, using the server-supplied intermediates and the trusted ISRG Root X1 anchor.
Error Messages/Logs
Steps to Reproduce
- On a Linux host whose root store relies on Claude Code's bundled set (or any environment exhibiting the failure), run
claudeand attempt OAuth login (or let an existing token refresh) after 2026-06-26. - Observe
OAuth error: SSL certificate error (UNABLE_TO_GET_ISSUER_CERT). - Confirm with
--debug: the OAuth refresh toplatform.claude.comfails whileapi.anthropic.comcalls only 401.
Claude Model
_No response_
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.195
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
_No response_
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗