MySQL Error 1396: Operation CREATE USER failed
MySQL error 1396 when creating a user usually means the user already exists but with a different host or in a dropped state. The fix is clean removal or a direct GRANT.
You type CREATE USER 'john'@'localhost' IDENTIFIED BY 'password' and MySQL spits back ERROR 1396 (HY000): Operation CREATE USER failed for 'john'@'localhost'. That's frustrating because you just tried to create a brand new user. But here's the thing: MySQL thinks that user already exists — and it might be right, just in a ghost state.
The fast fix
Run this right now:
DROP USER IF EXISTS 'john'@'localhost';
FLUSH PRIVILEGES;
CREATE USER 'john'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'john'@'localhost';
FLUSH PRIVILEGES;
After the DROP USER IF EXISTS you'll get a warning (not an error) if the user wasn't there — that's fine. After FLUSH PRIVILEGES you should see Query OK. Then the CREATE USER will work. Test it: SELECT User, Host FROM mysql.user WHERE User = 'john'; — you'll see the row now.
Why this happens
The root cause is almost always a partially dropped or deleted user. MySQL stores users in the mysql.user table. When you run DROP USER 'john'@'localhost', it removes that row. But if you instead ran DELETE FROM mysql.user WHERE User = 'john' (or someone scripted it badly), the row gets deleted from the table but the privileges cache still holds a ghost entry. Or the user was dropped from one host but another host entry remains. Error 1396 is MySQL's way of saying "I can't create this user because I still have a record of it somewhere — maybe with a different host."
Another common trigger: you previously created 'john'@'%' but now you're trying 'john'@'localhost'. These are two different MySQL account entries. If you DROP USER 'john'@'%' and then try to create 'john'@'localhost', MySQL might still have cached data that blocks it. Especially in MySQL 5.7 and earlier, the privilege tables got messy.
Less common variations
Case 1: The user exists but with a different host.
Check: SELECT User, Host FROM mysql.user WHERE User = 'john';
If you see 'john'@'%' and you want 'john'@'localhost', you don't need to create a new one — just grant privileges on the existing one. Or drop both and recreate.
Case 2: MySQL 8.0's authentication plugin mismatch.
If you see error 1396 with a user that definitely doesn't show in mysql.user, check the mysql.global_grants table:
SELECT * FROM mysql.global_grants WHERE User = 'john';
If you find a row there but no entry in mysql.user, you have a corrupt privilege system. Run mysql_upgrade or mysqlcheck --repair mysql user.
Case 3: The user was dropped but the authentication_string column is NULL.
In very old MySQL (5.1 and earlier), this caused ghost users. Today it's rare but shows up when restoring backups badly. The fix is the same: DROP USER IF EXISTS with all host variants.
Case 4: You're using a proxy user or role.
MySQL 8.0 introduced roles. If you try to create a user with the same name as an existing role, you get 1396. Check SELECT * FROM mysql.role_edges; — if the name appears there, drop the role first: DROP ROLE 'john';
Prevention
Never delete users directly from mysql.user with DELETE. Always use DROP USER. If you're scripting user creation, wrap it in a check:
DROP USER IF EXISTS 'app_user'@'localhost';
FLUSH PRIVILEGES;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'strong_password';
Also, keep an eye on your host definitions. If your app connects from localhost, use 'user'@'localhost' — don't use 'user'@'%' unless you really mean any host. They're separate accounts and MySQL treats them that way. When in doubt, run SELECT User, Host FROM mysql.user WHERE User = 'name'; before creating. That simple check saves you the 1396 headache every time.
Was this solution helpful?