ERROR 1045 is MySQL rejecting authentication for the specific user@host pair it resolved your connection to. The message quotes both halves, and the host half is where most of the confusion lives: MySQL treats 'app'@'localhost', 'app'@'10.0.0.5', and 'app'@'%' as entirely separate accounts, each with its own password and privileges. A password that works from one machine can fail from another because a different account matched. Read the quoted pair carefully before touching any passwords.
ERROR 1045 (28000): Access denied for user 'app'@'10.0.0.5' (using password: YES)
What this error means
When a connection arrives, MySQL sorts all accounts by host specificity and picks the first match for your username and source address. Authentication then runs against that one account only. Error 1045 means that account’s check failed: wrong password, an account that exists for a different host than the one you arrived from, an authentication plugin mismatch, or using password: NO because the client sent no password at all. The parenthetical matters: using password: NO means the credentials never reached the server, which points at the client configuration, not the account.
Common causes
- The password is wrong for the specific matched account, even if it is right for a same named account on another host.
- No account exists for the source host:
'app'@'localhost'exists but the app now connects over TCP from10.0.0.5. using password: NO: the client or a config file sent an empty password, often a missing environment variable that silently became an empty string.- MySQL 8’s
caching_sha2_passworddefault rejecting old client libraries that only speakmysql_native_password. - The connection arrives via a proxy, container gateway, or NAT, so the source IP MySQL sees is not the one you expect.
- Special characters in the password mangled by shell quoting or URL encoding in the connection string.
skip_name_resolvetoggled, changing whether hostname based accounts ('app'@'web1.internal') can ever match.
How to fix it
STABILIZE first. If production is locked out, identify the exact user@host in the error and reset that account’s password or create the missing account; both take effect immediately without a restart.
- DIAGNOSE: reproduce with the CLI so you control every variable, and note the exact quoted pair in the failure.
mysql --protocol=TCP -h db.internal -P 3306 -u app -p appdb
- DIAGNOSE: as an admin user, list which accounts actually exist for that username.
SELECT user, host, plugin FROM mysql.user WHERE user = 'app';
Compare the host values against the host quoted in the error. If nothing matches the source (for example the error says 'app'@'10.0.0.%' territory but only 'app'@'localhost' exists), the account is missing, not the password.
- FIX a missing account: create it for the network the application really connects from, and grant only what it needs.
CREATE USER 'app'@'10.0.0.%' IDENTIFIED BY 'use-a-generated-secret';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'10.0.0.%';
FLUSH PRIVILEGES;
- FIX a wrong password: reset it for the exact pair that matched.
ALTER USER 'app'@'10.0.0.%' IDENTIFIED BY 'use-a-generated-secret';
- FIX a plugin mismatch: if the client library predates MySQL 8, prefer upgrading the driver. If you need immediate compatibility, switch that one account’s plugin.
ALTER USER 'app'@'10.0.0.%' IDENTIFIED WITH mysql_native_password BY 'use-a-generated-secret';
- DIAGNOSE
using password: NO: the client sent no password. Check the application’s environment and any option files the client silently reads.
mysql --print-defaults
Then verify the deployed environment variable is set in the running process, not just in your shell.
How to prevent it
- Manage MySQL users and grants in code (migrations or Terraform) so accounts exist for the right hosts on every environment.
- Standardize on one account host pattern per application (for example the VPC CIDR) instead of a mix of
localhost, IPs, and%. - Store passwords in a secrets manager and fail startup loudly when a credential resolves empty, so
using password: NOnever reaches the database. - Prefer upgrading client libraries over downgrading server authentication plugins.
- Grant least privilege per schema; never reuse the root account for applications.
- Log the resolved connection target and user at application startup so a misrouted connection is visible in seconds.
When to call a senior engineer
Call for help when logins fail only from some machines and the user@host matching does not add up, when a proxy or container network layer hides the true source address, or when an authentication plugin migration has to happen across a fleet of services without downtime. Erzon engineers can trace exactly which account each connection matches, clean up the accumulated user table, and leave your grants managed in code so access denied stops being a production surprise.
Related errors we fix
ERROR 1040 (HY000): Too many connections Fix this error → MySQL ERROR 1114 (HY000): The table is full Fix this error → MySQL ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction Fix this error → MySQL ERROR 1206 (HY000): The total number of locks exceeds the lock table size Fix this error → Want a senior engineer to just fix it?
Paste this exact error into the form. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.
Book a fix