I know that MongoServerError: bad auth: Authentication failed right after you tweaked your IP whitelist is infuriating — you changed one thing and suddenly everything's broken. The whitelist isn't actually what's rejecting you. Something about your connection string or user credentials is. Let's fix it.
The fast fix (2 minutes)
Ninety percent of the time this is one of two things: your password contains a character that needs URL-encoding, or your connection string is pointing at the wrong authSource. Check both.
Step 1: URL-encode your password
If your Atlas database user password has any of these characters, they break the URI:
@ : / ? # [ ] & %
An @ in your password is the classic offender. The driver reads everything after the first @ as the host. Encode it:
# Bad — password is p@ssw0rd
mongodb+srv://admin:p@ssw0rd@cluster0.abc12.mongodb.net/
# Good — @ becomes %40
mongodb+srv://admin:p%40ssw0rd@cluster0.abc12.mongodb.net/?retryWrites=true&w=majority
Use an encoder — I keep encodeURIComponent() in a Node REPL tab for exactly this:
node -e "console.log(encodeURIComponent('p@ssw0rd'))"
# p%40ssw0rd
Step 2: Add or verify authSource
Atlas database users live in the admin database by default. If your URI doesn't say so, the driver guesses and guesses wrong.
mongodb+srv://admin:p%40ssw0rd@cluster0.abc12.mongodb.net/mydb?retryWrites=true&w=majority&authSource=admin
That &authSource=admin is non-negotiable for Atlas users unless you created the user inside a specific app database (rare).
Step 3: Rotate the password and hard-restart
If encoding and authSource don't do it, go to Atlas → Database Access, edit the user, click Edit Password → Autogenerate, and copy the new one. Atlas passwords with symbols cause this exact error all the time. Then kill your app's connection pool:
# Node
pkill -f "node"
# Docker
docker compose restart app
# systemd
sudo systemctl restart myapp
Node's driver caches credentials. A hot reload won't pick up a new env var.
Why this happens right after an IP whitelist change
Here's the frustrating truth: the whitelist edit is a red herring. When you go into Atlas to add an IP, you end up staring at the Database Access tab too — and people often re-save the user, regenerate a password, or accidentally toggle the user's role. Meanwhile the app still has the old credentials in a running process.
Atlas also returns bad auth (error code 8000) for reasons that have nothing to do with networking:
- Wrong password in your
.env, CI secrets, or a Docker build arg - Password rotated in Atlas but not in your secrets manager
authSourcemissing from the URI- Special characters unencoded
- User created in a non-admin database
The IP whitelist failing would give you a timeout or MongooseServerSelectionError: connect ECONNREFUSED, not bad auth. If you're seeing bad auth, the network reached Atlas fine — Atlas just said "I don't know you."
Less common variations
1. You're using an API key as a password
Atlas Programmatic API Keys are not database credentials. Different system entirely. If someone handed you a string starting with something like abcdefgh and a matching private key, that's for the Admin API, not your cluster. Create a proper Database User.
2. LDAP or AWS IAM auth got mixed with SCRAM
If your cluster uses AWS IAM or LDAP for that user, the URI needs auth mechanism params:
mongodb+srv://cluster0.abc12.mongodb.net/?authMechanism=MONGODB-AWS
# or
?authMechanism=PLAIN&authSource=$external
SCRAM-SHA-256 (the default) will reject an IAM user every time.
3. Old driver + SCRAM-SHA-1 only user
Atlas deprecated SCRAM-SHA-1. If your app pins mongodb@3.x, upgrade to 4.x or 5.x. There's no workaround — the auth handshake won't complete.
4. Copy-pasted password with a trailing space
I've debugged this for three different clients. A space at the end of a password in a .env file is invisible in every editor I've used. Hex-dump it:
grep MONGO_URL .env | xxd | tail -3
5. Atlas free-tier cluster paused
M0/M2/M5 shared clusters pause after 60 days of inactivity. A paused cluster returns a different error normally, but if you're on a proxy that mangles errors, you might see bad auth. Log into the Atlas UI and check the cluster status indicator.
Prevention
Do these three things and you'll never hit this again:
- Store the full URI in one secret. Not "host + user + password as separate vars." One string. Assembled in your secrets manager, encoded correctly, tested once.
- Never autogenerate a password you can't URL-encode. When you create a user, click Edit Password and paste your own — letters, digits, and
- _ . ~only. No symbols needing encoding. - Pin
authSource=adminin the URI. Even if you switch projects or clusters, the string stays correct.
Also: when you change an IP whitelist, change only the IP whitelist. Don't click around. Atlas's UI has a nasty habit of treating a "save" on the Database Access tab as a user update.
If you've tried all of this, paste your connection string with the password replaced by REDACTED and your driver version. Nine times out of ten the answer's visible in the URI.