How to Fix npm SSL Certificate Errors on macOS: A Complete Troubleshooting Guide
The core question this article answers: Why does
npm installthrowUNABLE_TO_GET_ISSUER_CERT_LOCALLYorcurl: (77) error setting certificate verify locationson macOS — and how do you fix it for good?
The short answer: the root cause is a missing /etc/ssl/cert.pem file on your system. Regenerating it and wiring up NODE_EXTRA_CA_CERTS for Node.js resolves the issue end-to-end. This guide walks through the full diagnostic chain — from the first error message to a fully working runtime.
The Setup: One Package Install, Five Layers of Failure
What started as a routine global package installation:
npm i -g openclaw@2026.3.2
turned into a cascading series of errors: a broken npm registry, a missing SSL certificate file, a Git SSH permission denial, and finally a Node.js runtime that silently timed out on every HTTPS request — without ever mentioning certificates.
This is the nature of macOS development environments. Problems rarely travel alone. Each fix you apply surfaces the next issue waiting underneath.
If you’ve landed here because npm install is throwing SSL errors, or because your Node.js app keeps timing out on HTTPS requests for no obvious reason, you’re in the right place.
Issue 1: npm Registry Override Not Sticking
What this section answers: Why does npm keep hitting registry.npmjs.org even after you’ve set a custom registry?
This was the first hurdle. After running:
npm config set registry https://registry.npmmirror.com
the install error still pointed at the default registry:
npm error request to https://registry.npmjs.org/openclaw failed
The fix is to verify immediately — never assume the config saved:
npm config get registry
# Should return: https://registry.npmmirror.com
If it still shows https://registry.npmjs.org/, the setting didn’t persist. That usually means a project-level .npmrc or an environment variable is overriding your global config.
Permanent fix — edit the global config file directly:
open ~/.npmrc
Add this line:
registry=https://registry.npmmirror.com
Save, then re-run npm config get registry to confirm.
What I learned here: Always validate config changes on the spot. “It should have worked” is not the same as “it did work.” One quick verification check saves minutes of confused debugging.
Issue 2: The Missing CA Certificate File — The Real Root Cause
What this section answers: What does curl: (77) error setting certificate verify locations: CAfile: /etc/ssl/cert.pem CApath: none actually mean, and how do you fix it?
This error means curl cannot locate the system CA certificate bundle at /etc/ssl/cert.pem. Without this file, TLS handshakes fail, and every HTTPS request — from curl, npm, git, and Node.js — breaks down.
Check whether the file exists:
ls -la /etc/ssl/cert.pem
If you see:
ls: /etc/ssl/cert.pem: No such file or directory
That’s your culprit.
Why Does This File Go Missing on macOS?
macOS doesn’t natively depend on /etc/ssl/cert.pem — it uses its own Security Framework and Keychain. This path exists purely for compatibility with Unix-heritage tools like curl, OpenSSL, and Python. It can disappear due to:
-
A major macOS version upgrade resetting the /etc/ssl/directory -
Xcode Command Line Tools not being installed or needing a reinstall -
Switching between Homebrew OpenSSL versions, which handle certificate paths differently -
Manual system cleanup accidentally removing the file
Fix: Regenerate the Certificate File from macOS Keychain
# Create the directory if it doesn't exist
sudo mkdir -p /etc/ssl
# Export all trusted root certificates from the system Keychain
sudo security find-certificate -a -p \
/System/Library/Keychains/SystemRootCertificates.keychain \
| sudo tee /etc/ssl/cert.pem
Verify the fix:
ls -la /etc/ssl/cert.pem
# Expected output: -rw-r--r-- 1 root wheel 235703 ...
# Test with curl
curl https://dashscope.aliyuncs.com/compatible-mode/v1/models \
-H "Authorization: Bearer YOUR_API_KEY"
A valid JSON response confirms the certificate issue is resolved for curl and system-level tools.
Practical note: This fix applies broadly. Any CLI tool that respects the system CA bundle — curl, wget, git with HTTPS, Python’s urllib — will immediately benefit from this repair.
Issue 3: npm’s Independent SSL Verification
What this section answers: curl works fine now, so why is npm still throwing UNABLE_TO_GET_ISSUER_CERT_LOCALLY?
npm uses Node.js’s TLS implementation, which does not automatically inherit the system /etc/ssl/cert.pem path. It has its own certificate resolution logic. Fixing the system file alone isn’t enough — you need to point npm at it explicitly.
Recommended fix — point npm at the repaired certificate:
npm config set cafile /etc/ssl/cert.pem
This is the safest, most permanent solution. npm will use the certificate file you just rebuilt.
Temporary workaround — disable strict SSL (for emergencies only):
npm config set strict-ssl false
npm i -g openclaw@2026.3.2
# Restore immediately after
npm config set strict-ssl true
⚠️ Do not leave
strict-ssl falseas a permanent setting. It disables all certificate validation for npm, exposing every package install to potential man-in-the-middle attacks. Use it only to unblock a stuck install, then restore it right away.
Issue 4: Git SSH Dependencies Inside npm Packages
What this section answers: Why would an npm package install trigger a Git SSH error about GitHub?
With npm’s SSL verification resolved, the install made progress — then hit a new wall:
npm error code 128
npm error command git --no-replace-objects ls-remote ssh://git@github.com/whiskeysockets/libsignal-node.git
npm error git@github.com: Permission denied (publickey).
This means one of openclaw‘s transitive dependencies declares its source as an SSH GitHub URL in its package.json. On a machine without a GitHub SSH key configured, that lookup fails immediately.
Fix: Force git to substitute HTTPS for SSH URLs globally:
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
This tells git to transparently rewrite any ssh://git@github.com/ URL to https://github.com/, no SSH key required.
The follow-on problem — git’s own certificate issue:
Switching to HTTPS exposed the same certificate problem in git:
fatal: unable to access 'https://github.com/...': error setting certificate verify locations:
CAfile: /etc/ssl/cert.pem CApath: none
Fix: Point git at the repaired certificate:
git config --global http.sslCAInfo /etc/ssl/cert.pem
This is the clean, permanent solution — more reliable than disabling git’s SSL verification entirely.
Reflection: SSH URLs buried inside transitive npm dependencies are a common but invisible footgun, especially in environments where GitHub SSH keys aren’t set up by default. The insteadOf config is one of those git tricks that’s rarely needed — until suddenly it’s the only thing standing between you and a working install.
Issue 5: Node.js Has Its Own Certificate Isolation
What this section answers: The package installed successfully, but the Node.js app keeps timing out on HTTPS requests — could this still be a certificate problem?
Yes, and this one is the trickiest to diagnose.
After openclaw installed cleanly, launching it with openclaw gateway produced:
LLM request timed out.
The app was configured to use the Alibaba Cloud DashScope API — a domestic Chinese endpoint, no proxy required:
https://dashscope.aliyuncs.com/compatible-mode/v1
curl reached it fine. The API key was valid. The model names were correct. Yet Node.js kept timing out.
The reason: Node.js ships with its own bundled CA certificate list and does not automatically inherit the system CA bundle. When the system certificate file is missing or has been recently regenerated, Node.js processes — including long-running servers and CLI tools — may fail to establish TLS connections, manifesting as timeouts rather than explicit certificate errors.
This makes it particularly hard to diagnose. Nothing in the error message says “certificate.” It just says “timed out.”
Fix: NODE_EXTRA_CA_CERTS
export NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem
Make it permanent in ~/.zshrc:
echo 'export NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem' >> ~/.zshrc
source ~/.zshrc
Then relaunch openclaw gateway. The timeouts stop.
How NODE_EXTRA_CA_CERTS works: This environment variable appends the certificates in the specified file to Node.js’s built-in CA list — it does not replace them. Your app gains full trust coverage: Node’s default roots plus any system-specific certificates. It’s the safest way to extend Node.js certificate trust without disabling verification.
Important distinction:
NODE_EXTRA_CA_CERTSis safe and appropriate for production use.NODE_TLS_REJECT_UNAUTHORIZED=0— which fully disables TLS verification in Node.js — is not. Never use the latter outside of local debugging.
Certificate Configuration: Tool-by-Tool Reference
Each major CLI tool on macOS manages certificate trust independently. Here’s a consolidated view:
| Tool | Certificate Source | How to Fix |
|---|---|---|
| curl | System /etc/ssl/cert.pem |
Regenerate the file from Keychain |
| npm | Built-in + cafile config |
npm config set cafile /etc/ssl/cert.pem |
| git | System + http.sslCAInfo config |
git config --global http.sslCAInfo /etc/ssl/cert.pem |
| Node.js | Built-in bundle | NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem |
| Python requests | certifi package |
pip install --upgrade certifi or REQUESTS_CA_BUNDLE |
| Homebrew OpenSSL | /opt/homebrew/etc/openssl@3/cert.pem |
brew install ca-certificates |
The key insight: fixing the system certificate file is necessary but not sufficient. Each tool needs to be pointed at that file independently.
Complete Fix: Step-by-Step Execution Order
Run these in sequence. Verify each step before moving to the next.
Step 1: Regenerate the System CA Certificate File
sudo mkdir -p /etc/ssl
sudo security find-certificate -a -p \
/System/Library/Keychains/SystemRootCertificates.keychain \
| sudo tee /etc/ssl/cert.pem
# Verify
ls -la /etc/ssl/cert.pem
Step 2: Configure npm
npm config set cafile /etc/ssl/cert.pem
npm config set registry https://registry.npmmirror.com
# Verify
npm config get registry
npm config get cafile
Step 3: Configure Git
git config --global http.sslCAInfo /etc/ssl/cert.pem
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
Step 4: Configure Node.js (Permanent)
echo 'export NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem' >> ~/.zshrc
source ~/.zshrc
Step 5: Test Everything
# Test curl
curl https://www.google.com
# Test npm install
npm i -g openclaw@2026.3.2
# Test Node.js app
openclaw gateway
Validating an API Key and Endpoint (DashScope Example)
If you’re configuring a Node.js tool against an LLM API and want to verify connectivity independently of the app, use this pattern:
# Single-line curl — avoids shell escaping issues with backslash line breaks
curl "https://dashscope.aliyuncs.com/compatible-mode/v1/models" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
A successful response returns a JSON array of available models. If you see a 401 with “You didn’t provide an API key,” the request structure is wrong (check for shell escaping issues). If you see a certificate error, the CA bundle isn’t in place yet.
Author’s Reflection
The thing that surprised me most about this entire chain of errors wasn’t the number of them — it was how differently the same root cause presented across tools.
-
curl said: error setting certificate verify locations -
npm said: UNABLE_TO_GET_ISSUER_CERT_LOCALLY -
git said: fatal: unable to access … error setting certificate verify locations -
Node.js said: LLM request timed out
Three tools at least told me something about certificates. Node.js gave me nothing. That single timeout message could mean a dead server, a firewall, a wrong URL, an invalid API key, a network routing issue — or a missing CA bundle. It took eliminating every other possibility to land on the certificate as the cause.
The practical takeaway: whenever you’re debugging HTTPS failures in a Node.js application and the usual suspects come up empty, run a quick cert check. It’s a five-second sanity test that’s easy to overlook.
The second lesson: verify config changes immediately. Don’t trust that npm config set worked. Don’t trust that git config applied. Check with the corresponding get command right after. A configuration that looks like it saved but didn’t is harder to debug than an error that never went silent.
Quick-Reference Checklist
-
[ ] Verify /etc/ssl/cert.pemexists:ls -la /etc/ssl/cert.pem -
[ ] Regenerate if missing: sudo security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain | sudo tee /etc/ssl/cert.pem -
[ ] Point npm at certificate: npm config set cafile /etc/ssl/cert.pem -
[ ] Set npm registry (if using a mirror): npm config set registry https://registry.npmmirror.com -
[ ] Point git at certificate: git config --global http.sslCAInfo /etc/ssl/cert.pem -
[ ] Fix SSH-to-HTTPS for GitHub (if needed): git config --global url."https://github.com/".insteadOf ssh://git@github.com/ -
[ ] Set Node.js cert env var (permanent): echo 'export NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem' >> ~/.zshrc && source ~/.zshrc -
[ ] Test curl: curl https://www.google.com -
[ ] Test npm: npm i -g <package-name> -
[ ] Test Node.js app behavior after source ~/.zshrc
One-Page Summary
| Symptom | Root Cause | Fix |
|---|---|---|
curl: (77) error setting certificate verify locations |
/etc/ssl/cert.pem missing |
Regenerate from Keychain with security find-certificate |
npm UNABLE_TO_GET_ISSUER_CERT_LOCALLY |
npm can’t find trusted CA | npm config set cafile /etc/ssl/cert.pem |
git: Permission denied (publickey) |
Dependency uses SSH GitHub URL | git config --global url."https://github.com/".insteadOf ssh://git@github.com/ |
git: error setting certificate verify locations |
git SSL cert path broken | git config --global http.sslCAInfo /etc/ssl/cert.pem |
| Node.js app request timeout (HTTPS) | Node.js certificate isolation | export NODE_EXTRA_CA_CERTS=/etc/ssl/cert.pem |
| npm registry override not sticking | Config not persisted globally | Edit ~/.npmrc directly, verify with npm config get registry |
FAQ
Q: Do I need to restart my terminal after regenerating /etc/ssl/cert.pem?
No — the file change takes effect immediately for curl and system-level tools. However, if you’re adding NODE_EXTRA_CA_CERTS to ~/.zshrc, you’ll need to run source ~/.zshrc or open a new terminal window before it applies to new Node.js processes.
Q: Is it safe to keep npm config set strict-ssl false permanently?
No. This disables all certificate validation for npm, which means every package you install is vulnerable to interception or tampering. Use it only as a temporary unblock during troubleshooting, then immediately restore it with npm config set strict-ssl true.
Q: What’s the difference between NODE_EXTRA_CA_CERTS and NODE_TLS_REJECT_UNAUTHORIZED=0?
NODE_EXTRA_CA_CERTS appends additional trusted certificates to Node.js’s existing bundle — it’s safe and appropriate for production environments. NODE_TLS_REJECT_UNAUTHORIZED=0 completely disables TLS verification, which is a security risk. Never use the latter outside of isolated local debugging.
Q: Why does npm sometimes ignore my custom registry and fall back to the default?
A project-level .npmrc file in your current directory takes precedence over the global config. Also check whether the environment variable NPM_CONFIG_REGISTRY is set, which would override everything. Run npm config list to see all active config sources and their priorities.
Q: Will this fix also help with Python requests SSL errors?
Not directly. Python’s requests library uses certifi‘s bundled certificates, not the system CA bundle. For Python SSL errors, run pip install --upgrade certifi. If you need to point it at the system bundle, set the environment variable REQUESTS_CA_BUNDLE=/etc/ssl/cert.pem.
Q: How do I know if Homebrew’s OpenSSL is causing the conflict instead?
Check whether Homebrew’s cert file exists: ls /opt/homebrew/etc/openssl@3/cert.pem (Apple Silicon) or ls /usr/local/etc/openssl/cert.pem (Intel). If tools installed via Homebrew fail even after the system fix, run brew install ca-certificates to update Homebrew’s cert bundle independently.
Q: Should I expect this problem to come back after future macOS updates?
Possibly, after major version upgrades. The safest habit is to run ls -la /etc/ssl/cert.pem right after any macOS major update. If the file is missing, the regeneration command takes under ten seconds to run. Having NODE_EXTRA_CA_CERTS set in your .zshrc also provides a soft safeguard — as long as the file itself exists, Node.js will use it automatically.
Q: What if I’m on an Intel Mac — do the paths change?
The /etc/ssl/cert.pem path and all the commands in this guide are the same on both Intel and Apple Silicon Macs. The only difference is Homebrew’s OpenSSL path: Intel Macs use /usr/local/etc/openssl/ while Apple Silicon uses /opt/homebrew/etc/openssl@3/.

