OneLinersCommand workbench
Guides
Databases & Data / Identity & Access / Incident Response & Troubleshooting

Fix MySQL ERROR 1045 Access denied for user

Diagnose MySQL ERROR 1045 by proving the selected server, transport, client origin, matching user@host account, authentication plugin, lock state, password state, and TLS policy before changing credentials or grants.

40 min8 stepsChanges system stateRevision 1
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Restore the intended MySQL account over the intended encrypted connection without creating a wildcard account, weakening TLS, exposing a password, or granting broader database privileges.

Supported environments
  • MySQL Server 8.4 LTS
  • MySQL client 8.4
Prerequisites
  • Literal failure Keep the complete ERROR 1045 line, the original client command with secrets removed, the UTC time, and whether the client reported `using password: YES` or `NO`.
  • Independent administration path Retain an approved local socket or break-glass administrative session before changing the affected account. Do not test recovery by editing the only working administrator.sudo mysql --protocol=SOCKET --execute="SELECT CURRENT_USER(), @@hostname, @@version;"
  • Account ownership Know the intended application or person, client source range, schema, required privileges, secret owner, and whether the account must require TLS or a client certificate.
  • Trusted CA For remote verification, have the approved CA file and a DNS name present in the server certificate. Do not replace identity verification with `--ssl-mode=DISABLED`.
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A repeatable MySQL incident record that starts with the literal `ERROR 1045 (28000): Access denied for user 'name'@'host' (using password: YES)` symptom and preserves the first useful failure instead of hiding it with an early restart or broad permission change.
  • A layer-by-layer decision path from client context through configuration and runtime evidence to one narrowly scoped remediation, followed by positive and negative verification.
  • A reusable evidence bundle containing commands, concrete example output, timestamps, identities, effective configuration, and stop conditions that another operator can review without access to the original terminal.
Observable outcome
  • You can identify which boundary failed, explain why competing hypotheses were rejected, and state what changed before declaring the incident resolved.
  • The repaired path succeeds under the intended identity while an unauthorized or incorrect path still fails, proving that the fix did not simply remove a security control.
  • The final evidence distinguishes a transient recovery from a durable repair by checking logs, counters, configuration provenance, and the original user-visible operation.

Architecture

How the parts fit together

MySQL troubleshooting is treated as an evidence pipeline rather than a list of guesses. The operator captures the symptom, resolves client and target context, inspects the first authoritative server-side failure, tests the smallest cause boundary, applies one reviewed change, and repeats the same observation path. This keeps a secondary error from replacing the primary cause and makes rollback possible.

Client contextProves the executable, identity, target, selected configuration, environment variables, and exact request that produced the visible error.
Transport and targetConfirms that the request reached the intended host, socket, service, repository, or upstream instead of a stale context or similarly named environment.
Authoritative evidenceUses effective configuration, service state, event records, and logs from the component that made the decision rather than relying on a client summary.
Controlled remediationChanges one demonstrated cause, retains a backup or previous reference, and avoids global permission, trust, timeout, or force settings.
Verification boundaryRepeats the original operation, exercises a negative case, and watches for recurrence long enough to distinguish a repair from a restart-only recovery.
  1. Copy the literal error and the command that produced it before retrying, restarting, pruning, resetting, or editing anything.
  2. Resolve the selected identity, configuration, target, and current runtime state so later commands inspect the same path the user exercised.
  3. Read the nearest authoritative log or effective configuration and align timestamps across the client and server evidence.
  4. Test one hypothesis with a read-only command; if evidence disagrees, return to the previous layer instead of stacking speculative changes.
  5. Back up the affected configuration or reference, apply the narrow repair, validate syntax, and reload only the component that owns the decision.
  6. Repeat the original action plus a negative control, record the outcome, and keep rollback material until the observation window is complete.

Assumptions

  • The target runs Oracle MySQL 8.4 LTS; MariaDB account matching and authentication plugins differ and should use a separate version-specific procedure.
  • The operator can read account metadata through an approved administrative connection without displaying authentication hashes or application secrets.
  • The failing client should reach one known server directly while diagnosis is in progress; proxies and routers must be identified explicitly if they are part of the path.
  • The intended account already has an approved privilege and host policy, or a database owner can review the exact new account before it is created.
  • Clocks are close enough that client and server timestamps can be correlated. If they are not, record the offset before comparing logs.
  • The operator has a recovery path that does not depend on the component being changed, such as console access, a second session, or a preserved remote reference.
  • Commands are first run in the affected environment with placeholders reviewed. OneLiners never executes them and does not know local policy, tenancy, or maintenance constraints.

Key concepts

MySQL account
An identity is the pair `'user'@'host'`, not only a user name. Two rows with the same user and different host values are distinct accounts with independent credentials and policy.
Connection verification
Before any SQL privilege check, MySQL selects the first matching user table row, verifies credentials with that row's authentication plugin, and rejects locked accounts.
USER() and CURRENT_USER()
`USER()` reports the identity supplied by the client and observed origin; `CURRENT_USER()` reports the exact account row selected by the server after a successful login.
Authentication plugin
The account plugin defines how credentials are verified. MySQL 8.4 uses `caching_sha2_password` by default, while old clients may not support its exchange correctly.
TLS identity verification
`VERIFY_IDENTITY` checks both the issuing CA and the server name. `REQUIRED` encrypts traffic but does not prove that the certificate belongs to the intended host.
Set guide values0/7 ready

Fill these once. Every matching command and configuration block updates immediately; values stay in this page only.

Security and production boundaries

  • Never place MySQL passwords in command-line arguments, URLs, shell history, process environments, tickets, or copied example output. Use an interactive prompt, protected login path, or application secret manager.
  • Do not create `'user'@'%'`, grant `ALL ON *.*`, disable `require_secure_transport`, or use `--ssl-mode=DISABLED` as an authentication workaround.
  • Do not read or export `authentication_string`; password hashes are secrets and do not prove which cleartext credential the application currently holds.
  • Repeated ERROR 1045 events from unexpected sources may be credential stuffing or password spraying. Preserve logs and escalate instead of rotating blindly.
  • Do not paste private keys, tokens, complete environment dumps, authorization headers, or unredacted customer data into tickets or external analysis tools.
  • A successful operation after disabling authentication, trust, host verification, sandboxing, or least privilege is a security regression, not a valid repair.
  • Prefer effective configuration and narrow identity tests over recursive ownership changes, mode 777, global trust bypasses, or force options copied from unrelated incidents.

Stop before continuing if

  • Stop if the target server UUID, observed client origin, or exact matching user@host row is not known.
  • Stop if the only recovery proposal disables TLS, weakens the authentication plugin, creates a wildcard host account, or grants global privileges.
  • Stop if no independent administrative connection or rollback definition remains available.
  • Stop and start a security incident if failures come from unexpected networks, affect multiple identities, or continue after a controlled credential rotation.
  • Stop if the target, account, environment, repository, or service instance cannot be identified unambiguously.
  • Stop before a destructive cleanup, force update, broad permission change, or production reload when backup and recovery evidence is missing.
  • Stop if the proposed action would conceal the first error or remove logs, failed objects, repository references, or configuration needed for diagnosis.
01

instruction

Freeze the symptom and define the incident boundary

read-only

Before changing MySQL, preserve the literal `ERROR 1045 (28000): Access denied for user 'name'@'host' (using password: YES)` message, the exact action that produced it, the affected identity, target, UTC time, and expected result. Decide which production boundary is in scope and name the independent recovery path you will keep available.

Why this step matters

A retry, restart, cleanup, permission change, or configuration edit can replace the primary failure with a secondary one. A fixed starting record lets every later check answer a specific hypothesis.

What to understand

Record whether the problem affects one user or workload, one target, one host, or every comparable path; this determines whether to begin at the client, transport, or service boundary.

Write down the expected successful behavior in observable terms such as an exit status, HTTP response, remote object ID, authenticated principal, or stable service state.

Keep console access, a second session, an earlier configuration, or a recoverable reference independent of the component being diagnosed.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.
Example output / evidence
Incident scope
Tool: MySQL
Observed: `ERROR 1045 (28000): Access denied for user 'name'@'host' (using password: YES)`
Affected target: production-app-01
Observed at: 2026-08-17T14:02:11Z
Expected: the reviewed operation completes without weakening an access or safety control
Recovery path: console session and last known-good configuration retained

Security notes

  • Redact secrets and customer data, but retain error text, timestamps, object IDs, modes, owners, and target names needed to reproduce the decision.

Alternatives

  • When direct production inspection is restricted, reproduce the same version and configuration boundary in an isolated environment and mark which evidence is illustrative.

Stop conditions

  • Stop before any mutation if the target, identity, affected environment, expected result, or recovery path is still ambiguous.
02

command

Reproduce the exact failure from the real client boundary

read-only

Use the same host name, port, protocol, account, CA, and network origin as the failing application. Prompt for the password instead of placing it in the command line, and preserve the complete ERROR 1045 text.

Why this step matters

The error already identifies whether a password was supplied and the client origin MySQL used for account matching. Reproducing the real route prevents a successful local socket test from being mistaken for proof that the application path works.

What to understand

Compare the host shown inside the error with the expected application source or proxy address. Unexpected NAT, containers, a jump host, or a database proxy can select another account row.

Review `mysql --print-defaults` for option files that silently replace the host, user, socket, protocol, or TLS mode, but never copy a printed secret into the incident record.

Record the command, UTC timestamp, exit status, and the exact output before moving to the next layer. A later successful command must not erase evidence of the original failure.

Interpret this result together with the previous checkpoint. One matching line is evidence for a hypothesis, not permission to apply every commonly suggested fix.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.

Syntax explained

--protocol=TCP
Prevents localhost socket fallback so the test exercises the same network account-matching boundary as the application.
--password
Prompts interactively; a password directly after this option may be visible to process listings and shell history.
--ssl-mode=VERIFY_IDENTITY
Requires encryption, validates the issuing CA, and checks the DNS host against the server certificate.
--connect-timeout=5
Bounds the connection attempt without changing server timeout policy.
Command
Fill variables0/4 ready

Values stay on this page and are never sent or saved.

mysql --version && mysql --print-defaults && mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --connect-timeout=5 --execute="SELECT 1;"
Example output / evidence
mysql  Ver 8.4.6 for Linux on x86_64 (MySQL Community Server - GPL)
mysql would have been started with the following arguments:
--host=db01.example.net --user=app_reader
Enter password:
ERROR 1045 (28000): Access denied for user 'app_reader'@'10.20.30.44' (using password: YES)

Checkpoint: Checkpoint: Reproduce the exact failure from the real client boundary

mysql --version && mysql --print-defaults && mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --connect-timeout=5 --execute="SELECT 1;"

Continue whenThe failure is reproducible against the intended server and records the exact user, observed client host, password-use flag, client version, and TLS-verifying route.

Stop whenThe certificate identity fails, the server is not the intended instance, defaults select another account, or the source host differs from the approved network path.

If this step fails

The test reports ERROR 2002 or 2003 instead of ERROR 1045.

Likely causeThe client cannot reach the selected listener or socket, so authentication has not started.

Safe checks
  • getent ahosts {{dbHost}}
  • nc -vz -w 3 {{dbHost}} {{dbPort}}

ResolutionMove to the dedicated MySQL connection-failure procedure. Do not change account credentials while transport is unproven.

Security notes

  • Do not use `MYSQL_PWD`, a password in the command line, or `--ssl-mode=DISABLED` to make the reproduction convenient.

Alternatives

  • When the application runs in a container or orchestrator, execute the same client version from an approved diagnostic container in that workload's namespace.

Stop conditions

  • The certificate identity fails, the server is not the intended instance, defaults select another account, or the source host differs from the approved network path.
03

command

Inventory every candidate user@host account safely

read-only

From the independent administrative session, list only non-secret account metadata for the affected user and server policies that influence host and transport matching.

Why this step matters

MySQL authenticates one exact account row. A correct password for `app_reader@localhost` cannot authenticate `app_reader` arriving from 10.20.30.44 when a different host row is selected.

What to understand

Do not select `authentication_string`; password hashes are secrets and are unnecessary for deciding which row should match.

When `skip_name_resolve` is enabled, host names in account definitions do not provide the intended DNS-based match. Prefer reviewed IP or subnet forms supported by the installed version.

Record the command, UTC timestamp, exit status, and the exact output before moving to the next layer. A later successful command must not erase evidence of the original failure.

Interpret this result together with the previous checkpoint. One matching line is evidence for a hypothesis, not permission to apply every commonly suggested fix.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.

Syntax explained

--protocol=SOCKET
Uses the retained local administrative recovery path instead of depending on the failing TCP account.
password_expired
Shows whether the account requires a password-change flow rather than an ordinary application login.
skip_name_resolve
Shows whether the server resolves client host names while matching accounts.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo mysql --protocol=SOCKET --batch --execute="SELECT User,Host,plugin,account_locked,password_expired FROM mysql.user WHERE User='{{dbUser}}' ORDER BY Host; SHOW VARIABLES WHERE Variable_name IN ('skip_name_resolve','require_secure_transport');"
Example output / evidence
User	Host	plugin	account_locked	password_expired
app_reader	10.20.30.0/255.255.255.0	caching_sha2_password	N	N
app_reader	localhost	caching_sha2_password	N	N
Variable_name	Value
require_secure_transport	ON
skip_name_resolve	ON

Checkpoint: Checkpoint: Inventory every candidate user@host account safely

sudo mysql --protocol=SOCKET --batch --execute="SELECT User,Host,plugin,account_locked,password_expired FROM mysql.user WHERE User='{{dbUser}}' ORDER BY Host; SHOW VARIABLES WHERE Variable_name IN ('skip_name_resolve','require_secure_transport');"

Continue whenOne reviewed candidate account matches the user and observed client origin, and its lock, expiry, plugin, and transport policy are explicit.

Stop whenNo account is owned by the affected workload, multiple overlapping rows are unexplained, or the only proposed repair is a broad `%` host entry.

If this step fails

No row exists for the affected user and source boundary.

Likely causeThe account was never created on this server, was removed, exists only on another instance, or the workload now originates from a different network.

Safe checks
  • sudo mysql --protocol=SOCKET --execute="SELECT @@hostname, @@server_uuid;"
  • sudo mysql --protocol=SOCKET --execute="SELECT User,Host FROM mysql.user ORDER BY User,Host;"

ResolutionVerify the intended server and account owner, then create one least-privilege host-specific account through change control instead of cloning an unrelated row.

Security notes

  • Restrict access to mysql.user metadata and redact unrelated account names from shared incident records.

Alternatives

  • Use an approved database administration interface that exposes account name, plugin, lock, expiry, and TLS requirements without authentication hashes.

Stop conditions

  • No account is owned by the affected workload, multiple overlapping rows are unexplained, or the only proposed repair is a broad `%` host entry.
04

command

Inspect the exact account definition and grants

read-only

Read the server-generated account statement and grants for the candidate host row. This distinguishes credential, plugin, lock, TLS, and authorization policy without editing grant tables directly.

Why this step matters

`SHOW CREATE USER` is the authoritative, reproducible view of authentication and account policy, while `SHOW GRANTS` proves authorization separately. ERROR 1045 occurs during connection verification, before schema privileges are evaluated.

What to understand

Do not grant database privileges to solve a connection-stage credential or host mismatch; those privileges are checked only after authentication succeeds.

Compare the account plugin with the failing client version. Upgrading an obsolete client is safer than downgrading an account to a deprecated authentication plugin.

Record the command, UTC timestamp, exit status, and the exact output before moving to the next layer. A later successful command must not erase evidence of the original failure.

Interpret this result together with the previous checkpoint. One matching line is evidence for a hypothesis, not permission to apply every commonly suggested fix.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.

Syntax explained

SHOW CREATE USER
Displays authentication plugin, TLS requirements, lock state, password policy, and other account attributes as a reproducible statement.
SHOW GRANTS
Displays privilege and role assignments without modifying the account.
\G
Requests vertical mysql-client output so a long account definition remains readable.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

sudo mysql --protocol=SOCKET --execute="SHOW CREATE USER '{{dbUser}}'@'{{matchedHost}}'; SHOW GRANTS FOR '{{dbUser}}'@'{{matchedHost}}';"
Example output / evidence
CREATE USER for app_reader@10.20.30.0/255.255.255.0	CREATE USER `app_reader`@`10.20.30.0/255.255.255.0` IDENTIFIED WITH 'caching_sha2_password' REQUIRE SSL ACCOUNT UNLOCK
Grants for app_reader@10.20.30.0/255.255.255.0	GRANT USAGE ON *.* TO `app_reader`@`10.20.30.0/255.255.255.0` REQUIRE SSL
Grants for app_reader@10.20.30.0/255.255.255.0	GRANT SELECT ON `appdb`.* TO `app_reader`@`10.20.30.0/255.255.255.0`

Checkpoint: Checkpoint: Inspect the exact account definition and grants

sudo mysql --protocol=SOCKET --execute="SHOW CREATE USER '{{dbUser}}'@'{{matchedHost}}'; SHOW GRANTS FOR '{{dbUser}}'@'{{matchedHost}}';"

Continue whenThe selected account has the intended plugin, lock and expiry state, TLS requirement, roles, and schema-scoped privileges.

Stop whenThe account definition differs from the approved policy, the plugin is unsupported by the real client, or grants are broader than the workload requires.

If this step fails

SHOW CREATE USER reports that the account does not exist.

Likely causeThe inspected host part differs from the actual account row or the wrong server is being administered.

Safe checks
  • sudo mysql --protocol=SOCKET --execute="SELECT User,Host FROM mysql.user WHERE User='{{dbUser}}';"
  • sudo mysql --protocol=SOCKET --execute="SELECT @@hostname, @@server_uuid;"

ResolutionCopy the exact Host value from the account inventory and verify the server identity; do not omit the host because omission defaults to `%` in account statements.

Security notes

  • Treat account definitions and grants as sensitive infrastructure metadata even when password hashes are redacted.

Alternatives

  • Use `SHOW CREATE USER CURRENT_USER` and `SHOW GRANTS FOR CURRENT_USER` from a comparable successful account when administrative access to other accounts is intentionally restricted.

Stop conditions

  • The account definition differs from the approved policy, the plugin is unsupported by the real client, or grants are broader than the workload requires.
05

command

Correlate the client failure with server-side evidence

read-only

Inspect the MySQL service journal and configured error-log destination for the same UTC window. Use logs to confirm target, source, and policy, but do not assume every failed login is logged at the current verbosity.

Why this step matters

A timestamp-aligned server record proves the client reached this MySQL instance and may reveal the source identity or policy boundary omitted by an application wrapper.

What to understand

Match UTC timestamps, connection origin, user, proxy or router path, and server identity. A similar error from another workload is not evidence for this incident.

Do not raise global log verbosity or install an authentication plugin during an outage without reviewing data volume, privacy, performance, and rollback.

Record the command, UTC timestamp, exit status, and the exact output before moving to the next layer. A later successful command must not erase evidence of the original failure.

Interpret this result together with the previous checkpoint. One matching line is evidence for a hypothesis, not permission to apply every commonly suggested fix.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.

Syntax explained

SHOW VARIABLES
Finds the active error-log destination and verbosity from the running server rather than assuming a distribution path.
journalctl --since
Restricts service records to the incident window while preserving timestamps and unit identity.
Command
sudo mysql --protocol=SOCKET --batch --execute="SHOW VARIABLES WHERE Variable_name IN ('log_error','log_error_verbosity');" && sudo journalctl -u mysql --since '15 minutes ago' --no-pager | grep -Ei 'access denied|aborted connection|authentication|ssl' | tail -n 80
Example output / evidence
Variable_name	Value
log_error	/var/log/mysql/error.log
log_error_verbosity	2
Aug 18 11:42:08 db01 mysqld[1842]: [Warning] Access denied for user 'app_reader'@'10.20.30.44' (using password: YES)

Checkpoint: Checkpoint: Correlate the client failure with server-side evidence

sudo mysql --protocol=SOCKET --batch --execute="SHOW VARIABLES WHERE Variable_name IN ('log_error','log_error_verbosity');" && sudo journalctl -u mysql --since '15 minutes ago' --no-pager | grep -Ei 'access denied|aborted connection|authentication|ssl' | tail -n 80

Continue whenServer identity and the incident timestamp align with the client error, or the absence of a record is explicitly treated as inconclusive.

Stop whenLogs point to another server, another client origin, repeated password spraying, or a wider authentication incident requiring security response.

If this step fails

No access-denied entry appears in the journal.

Likely causeThe server logs to another destination, verbosity omits the event, the client reached another instance, or a proxy generated the visible error.

Safe checks
  • sudo mysql --protocol=SOCKET --execute="SHOW VARIABLES LIKE 'log_error';"
  • sudo ss -lntp 'sport = :3306'

ResolutionInspect the configured destination and identified proxy, then correlate connection counters and server identity. Do not claim that missing logs prove the attempt never arrived.

Security notes

  • Authentication logs can contain user names, client addresses, and workload topology; retain and share them according to incident policy.

Alternatives

  • Use an approved audit or observability platform that records the same server UUID, source, timestamp, and authentication result.

Stop conditions

  • Logs point to another server, another client origin, repeated password spraying, or a wider authentication incident requiring security response.
06

decision

Apply one reviewed account repair through an interactive session

caution

Choose exactly one demonstrated cause. Unlock the intended row, rotate its password through the secret manager, create a precise host-specific replacement, or upgrade the client. Preserve the old account definition and grants before any SQL change.

Why this step matters

An interactive administrative session prevents the new password from appearing in process arguments and makes the operator review the exact `ALTER USER`, `CREATE USER`, `GRANT`, or `ACCOUNT UNLOCK` statement before execution.

What to understand

For a proven credential mismatch, use `ALTER USER 'name'@'host' IDENTIFIED BY '<secret>' RETAIN CURRENT PASSWORD`, deploy the new secret, test new sessions, then discard the old password after the rollback window when policy and version support dual passwords.

For a proven host mismatch, create one exact reviewed account row and copy only approved roles or schema grants. Never solve it with an unexplained `'user'@'%'` account.

For an unsupported authentication plugin, upgrade the client or driver first. Do not switch production accounts to deprecated `mysql_native_password` as a convenience fix.

Account-management statements update grant state themselves; do not edit mysql.user directly and do not add `FLUSH PRIVILEGES` to hide an uncertain change.

Record the command, UTC timestamp, exit status, and the exact output before moving to the next layer. A later successful command must not erase evidence of the original failure.

Interpret this result together with the previous checkpoint. One matching line is evidence for a hypothesis, not permission to apply every commonly suggested fix.

System changes

  • May change one account's credential, lock state, authentication policy, or create one reviewed host-specific account. It must not alter global server authentication, TLS, or unrelated grants.

Syntax explained

sudo mysql --protocol=SOCKET
Opens the independent local administrative path without putting the replacement secret in shell history or process arguments.
RETAIN CURRENT PASSWORD
Keeps the previous password temporarily during a coordinated rotation when supported and approved, providing a bounded rollback window.
ACCOUNT UNLOCK
Unlocks only the named account after the lock cause and failed-login activity have been reviewed.
Command
sudo mysql --protocol=SOCKET
Example output / evidence
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4821
Server version: 8.4.6 MySQL Community Server - GPL
mysql>

Checkpoint: Checkpoint: Apply one reviewed account repair through an interactive session

sudo mysql --protocol=SOCKET

Continue whenOne approved user@host row changes, its previous definition and grants remain recoverable, and no TLS or privilege boundary is weakened.

Stop whenThe exact matching row, account owner, secret delivery path, rollback statement, or least-privilege grant set is not approved.

If this step fails

The repair succeeds for one client but breaks another workload.

Likely causeMultiple workloads shared one account, depended on the old password, or arrived through different host rows.

Safe checks
  • sudo mysql --protocol=SOCKET --execute="SHOW CREATE USER '{{dbUser}}'@'{{matchedHost}}';"
  • sudo mysql --protocol=SOCKET --execute="SHOW GRANTS FOR '{{dbUser}}'@'{{matchedHost}}';"

ResolutionUse the retained password or restore the captured account definition, then separate workload identities and coordinate the rotation instead of stacking more changes.

Security notes

  • Generate, distribute, and retire secrets through the approved secret manager. Never paste a real password into this guide, a command-line argument, chat, or ticket.

Alternatives

  • When no safe production mutation window exists, create a separate temporary least-privilege account for a controlled validation and retire it after the owner approves the permanent repair.

Stop conditions

  • The exact matching row, account owner, secret delivery path, rollback statement, or least-privilege grant set is not approved.
07

command

Verify the matched account, TLS cipher, schema, and denial control

read-only

Reconnect from the real workload boundary with the updated protected secret. Prove both the supplied identity and server-selected account, confirm certificate-verified TLS, run one required read, and separately prove a forbidden administrative action still fails.

Why this step matters

A successful login alone is insufficient. The result must prove which account row MySQL selected, which server and schema answered, that TLS identity verification succeeded, and that least privilege remains enforced.

What to understand

Compare `USER()` with `CURRENT_USER()`; a difference is expected when a host pattern matches, but an unexpected blank or wildcard identity requires another account-matching review.

Run the application's smallest safe read or health query, then test an operation the account must not perform, such as creating a user or reading another schema, in a disposable or explicitly safe context.

Record the command, UTC timestamp, exit status, and the exact output before moving to the next layer. A later successful command must not erase evidence of the original failure.

Interpret this result together with the previous checkpoint. One matching line is evidence for a hypothesis, not permission to apply every commonly suggested fix.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.

Syntax explained

USER()
Shows the user name supplied by the client and the origin MySQL observed.
CURRENT_USER()
Shows the exact user@host account selected for authentication and privilege checks.
Ssl_cipher
A non-empty session value confirms that this connection is encrypted; VERIFY_IDENTITY has already checked CA and host name.
Command
Fill variables0/5 ready

Values stay on this page and are never sent or saved.

mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --database={{database}} --execute="SELECT USER() AS supplied_identity, CURRENT_USER() AS matched_account, @@hostname AS server, DATABASE() AS selected_database; SHOW SESSION STATUS LIKE 'Ssl_cipher'; SELECT 1 AS application_probe;"
Example output / evidence
Enter password:
supplied_identity	matched_account	server	selected_database
app_reader@10.20.30.44	app_reader@10.20.30.0/255.255.255.0	db01	appdb
Variable_name	Value
Ssl_cipher	TLS_AES_256_GCM_SHA384
application_probe
1

Checkpoint: Checkpoint: Verify the matched account, TLS cipher, schema, and denial control

mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --database={{database}} --execute="SELECT USER() AS supplied_identity, CURRENT_USER() AS matched_account, @@hostname AS server, DATABASE() AS selected_database; SHOW SESSION STATUS LIKE 'Ssl_cipher'; SELECT 1 AS application_probe;"

Continue whenThe intended account row authenticates from the real source, the verified server and schema answer, TLS has a cipher, the required query succeeds, and the negative control remains denied.

Stop whenCURRENT_USER identifies another row, the certificate cannot be verified, the required schema is wrong, or the account can perform an unapproved administrative or cross-schema action.

If this step fails

Authentication succeeds but the application still reports access denied.

Likely causeThe application uses another secret, host, pool, replica, container, option file, or long-lived connection that was not part of the manual test.

Safe checks
  • mysql --print-defaults
  • sudo mysql --protocol=SOCKET --execute="SHOW PROCESSLIST;"

ResolutionTrace one application connection to its exact server, source, configuration revision, and secret version. Restart or recycle only the affected connection pool after confirming the deployment state.

Security notes

  • Keep the negative control read-only or use a disposable schema; do not test privilege denial with a destructive production statement.

Alternatives

  • Use the application's built-in connection diagnostic when it reports USER(), CURRENT_USER(), server identity, schema, TLS, and secret revision without exposing the secret.

Stop conditions

  • CURRENT_USER identifies another row, the certificate cannot be verified, the required schema is wrong, or the account can perform an unapproved administrative or cross-schema action.
08

verification

Observe for recurrence and close with evidence

read-only

After the original MySQL operation succeeds, repeat the same read-only observation path used at the start. Watch the relevant logs, counters, identities, and target state for a bounded period, then record the proven cause, exact change, verification result, rollback point, and remaining uncertainty.

Why this step matters

A single successful retry may be a transient recovery. Repeating the original checks and retaining the rollback point distinguishes a durable repair from a restart-only improvement.

What to understand

Use the same client identity, target, path, and configuration selection as the original failure so the positive result is comparable.

Include one negative or unauthorized control where safe; this proves the repair did not work by removing authentication, trust, isolation, or branch protection.

Keep the evidence concise enough for another operator to reproduce, but include software versions and exact object or configuration references.

System changes

  • No persistent change. This step reads current configuration, identity, runtime state, or logs and records evidence for the incident timeline.
Example output / evidence
Observation window: 15 minutes
Original operation: PASS
Original signature recurrences: 0
Negative control: PASS
Rollback point retained: yes
Cause and repair recorded: yes
Remaining uncertainty: none observed within the stated boundary

Checkpoint: Checkpoint: the repair remains valid

mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --database={{database}} --execute="SELECT USER() AS supplied_identity, CURRENT_USER() AS matched_account, @@hostname AS server, DATABASE() AS selected_database; SHOW SESSION STATUS LIKE 'Ssl_cipher'; SELECT 1 AS application_probe;"

Continue whenThe intended operation stays healthy, the original signature does not recur, and the negative control still enforces the expected boundary.

Stop whenThe result depends on an unexplained retry, disabled control, different target, or unrecorded manual state.

If this step fails

The error returns during the observation window.

Likely causeThe change treated a symptom, another instance still has the old state, or an automated process reverted or recreated the failing condition.

Safe checks
  • mysql --version && mysql --print-defaults && mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --connect-timeout=5 --execute="SELECT 1;"
  • mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --database={{database}} --execute="SELECT USER() AS supplied_identity, CURRENT_USER() AS matched_account, @@hostname AS server, DATABASE() AS selected_database; SHOW SESSION STATUS LIKE 'Ssl_cipher'; SELECT 1 AS application_probe;"

ResolutionPreserve the recurrence, compare it with the first evidence set, restore the known-good state if necessary, and reopen the unresolved layer instead of stacking another repair.

Security notes

  • Store only redacted operational evidence and remove temporary debug output according to local retention policy after review.

Alternatives

  • Use the service's approved monitoring or audit trail when an interactive observation window is not practical.

Stop conditions

  • Do not close the incident while the result is intermittent, the rollback point is missing, or a safety control remains weakened.

Finish line

Verification checklist

Original operation succeedsmysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --database={{database}} --execute="SELECT USER() AS supplied_identity, CURRENT_USER() AS matched_account, @@hostname AS server, DATABASE() AS selected_database; SHOW SESSION STATUS LIKE 'Ssl_cipher'; SELECT 1 AS application_probe;"The operation completes with exit status 0 and without the original error. The output identifies the intended host, service, repository, or endpoint.
Failure evidence stays absentmysql --version && mysql --print-defaults && mysql --host={{dbHost}} --port={{dbPort}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --connect-timeout=5 --execute="SELECT 1;"A fresh diagnostic capture shows the healthy path and no recurrence of the original signature. Logs and counters remain stable during the observation window.

Recovery guidance

Common problems and safe checks

ERROR 1045 says using password: NO.

Likely causeThe client did not supply a password because the prompt, secret file, login path, environment injection, or application secret reference is missing.

Safe checks
  • mysql --print-defaults
  • mysql_config_editor print --all

ResolutionRestore the approved secret delivery mechanism and prompt or login path. Do not create a passwordless account to match a broken client.

The same credentials work on localhost but fail remotely.

Likely causeSocket and TCP connections match different user@host rows, or the remote source is transformed by NAT, a container, or a proxy.

Safe checks
  • sudo mysql --protocol=SOCKET --execute="SELECT User,Host,plugin FROM mysql.user WHERE User='{{dbUser}}';"
  • mysql --host={{dbHost}} --protocol=TCP --user={{dbUser}} --password --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}}

ResolutionKeep separate intentional local and remote account rows and align the remote row with the observed approved source; do not broaden it to `%`.

An old application reports Client does not support authentication protocol.

Likely causeThe driver cannot use the account's current authentication plugin or secure password exchange.

Safe checks
  • mysql --version
  • sudo mysql --protocol=SOCKET --execute="SHOW CREATE USER '{{dbUser}}'@'{{matchedHost}}';"

ResolutionUpgrade the driver or client and require verified TLS. Treat an authentication-plugin downgrade as an exceptional reviewed migration, not a quick fix.

The account is unlocked and the password is correct, but REQUIRE SSL still rejects the client.

Likely causeThe client uses plaintext, cannot validate the CA or host identity, or the account requires a client certificate, issuer, or subject that was not supplied.

Safe checks
  • mysql --ssl-mode=VERIFY_IDENTITY --ssl-ca={{caFile}} --host={{dbHost}} --user={{dbUser}} --password
  • sudo mysql --protocol=SOCKET --execute="SHOW CREATE USER '{{dbUser}}'@'{{matchedHost}}';"

ResolutionInstall the correct CA and client certificate material and preserve the account requirement. Do not disable TLS or remove REQUIRE to make the symptom disappear.

Login succeeds but queries return a database or table access-denied error.

Likely causeConnection verification is fixed, but the selected account lacks a role or object privilege required during Stage 2 authorization.

Safe checks
  • mysql --execute="SELECT CURRENT_USER(), CURRENT_ROLE(); SHOW GRANTS FOR CURRENT_USER();"
  • mysql --database={{database}} --execute="SELECT DATABASE();"

ResolutionIdentify the exact denied operation and add only the reviewed schema, table, routine, or role privilege. Keep it separate from ERROR 1045 diagnosis.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Create a separate purpose-specific identity for each workload so future rotations and host-policy changes do not affect unrelated clients.
  • Use an approved MySQL Router or database proxy only when its source identity, TLS termination, and backend account mapping are documented and observable.
  • For short-lived human administration, prefer an organization-approved identity plugin or bastion workflow over a shared long-lived password.

Operate it safely

  • Give each application and environment a distinct account, host boundary, role, and secret so USER()/CURRENT_USER() evidence maps cleanly to one owner.
  • Monitor failed connection rates by server and source without logging passwords, and alert separately on new origins, locked accounts, and TLS-policy failures.
  • Schedule credential rotation and client compatibility checks before deprecating authentication plugins or upgrading the MySQL server.
  • Turn the verified checks into a read-only health probe or alert using stable fields rather than matching a whole human-formatted line.
  • Record the cause, exact repair, rollback point, software version, and follow-up owner in the incident note so the same failure becomes cheaper to diagnose.
  • Review adjacent environments for the demonstrated cause, but apply changes only where the same evidence is present.

Reference

Frequently asked questions

Does ERROR 1045 mean the password is wrong?

Not necessarily. It can also mean the server selected another user@host row, the account is locked or expired, the authentication plugin and client are incompatible, or the account's TLS requirements were not met.

Why does app_reader@localhost work while the application fails?

A local socket normally matches a localhost account, while a remote TCP connection matches the observed client host. Those are different MySQL accounts even when the user name is identical.

Should I create app_reader@% to test?

No. A wildcard row can hide the real source mismatch and expands the credential's reachable boundary. Use the host displayed in the error and create only an approved precise row when one is genuinely missing.

Do I need FLUSH PRIVILEGES after ALTER USER or GRANT?

No. Account-management statements update the server's privilege state. FLUSH PRIVILEGES is associated with direct grant-table changes, which this procedure explicitly avoids.

Can I temporarily disable TLS to prove the password?

No. That changes the authentication boundary and may expose the credential. Verify the CA, host identity, account REQUIRE clause, and client certificate requirements instead.

What should a successful verification show?

The intended USER() origin, the expected CURRENT_USER() account row, the correct server and schema, a non-empty TLS cipher, a successful required query, and a failed negative privilege control.

Recovery

Rollback

Restore the captured account definition and grants, retain the previous password during the observation window when dual-password rotation is supported, and remove only the newly introduced host row or policy change.

  1. Use the protected pre-change SHOW CREATE USER and SHOW GRANTS evidence to reconstruct the prior authentication, lock, TLS, role, and privilege state through account-management statements.
  2. If rotation used RETAIN CURRENT PASSWORD, redeploy the previous secret while the retained password is still valid; do not discard either password until every dependent connection pool is verified.
  3. Lock a newly created replacement account before removing it, observe application and audit signals, then drop it only after the original account path is healthy.
  4. Repeat the real-client positive test and the negative privilege control after rollback, and record the exact user@host row selected by CURRENT_USER().

Evidence

Sources and review

Verified 2026-08-17Review due 2027-02-13
MySQL 8.4: Access deniedofficialMySQL 8.4: Access Control, Stage 1 — Connection VerificationofficialMySQL 8.4: Specifying Account NamesofficialMySQL 8.4: SHOW CREATE USERofficialMySQL 8.4: SHOW GRANTSofficialMySQL 8.4: Configuring Encrypted ConnectionsofficialMySQL 8.4: ALTER USERofficial