Three days. That’s how long one user couldn’t create an account on our site, while every test account sailed through the same form.
The Scene
The signup flow writes two tables in one transaction: users, and an old user_profiles table that a contractor’s dashboard still reads. Legacy, but it works. Her ticket said the page spun and threw a 500. (Support tickets describe bugs their own special way. “It doesn’t work” is a complete sentence, apparently.)
The Spiral
Day one I did what I always do: blame the newest code. We had shipped a validation tweak that Tuesday, so I rolled it back. Nothing. Then I tested the form with the obvious names: Deniz, Ayşe, Ali, plus one Turkish character for thoroughness. All fine. The logs had the actual error: Incorrect string value: '\xF0\x9F\x8C\x8A' for column 'display_name'. Googling that gives you a thousand charset posts, so I checked the users table: utf8mb4. Connection: utf8mb4. Theory dead, or so I decided. Day two I upgraded the MySQL driver for reasons I can’t defend. It did nothing, obviously.
Still with me? Day three is where I finally did the boring thing first.
The Discovery
Her name was in the ticket signature the entire time.
Deniz 🌊. I pasted it into the form, hit save, and the 500 came back instantly. One query againstinformation_schema.COLUMNSlater, two rows came back.users, utf8mb4, the table I had checked on day one. Anduser_profiles, plainutf8, the old 3-byte pretender that can’t store an emoji. A contractor set that up in 2023, and nobody audited it. The error names the column, never the table, and both tables have adisplay_name. I had checked one of two.
For the record, \xF0\x9F\x8C\x8A is 🌊. Deniz means sea in Turkish. Day one I had even tested the name Deniz. Just without the part that mattered.
The Fix
One statement, a few seconds on 60,000 rows, zero drama:
ALTER TABLE user_profiles CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
She signed up that evening. I’m not proud of the driver upgrade, or of the two days spent interrogating innocent code.
The Lesson
- utf8 in MySQL was never UTF-8. It’s a 3-byte relic from 2003 that can’t hold an emoji. A table still on
utf8is a bug waiting for its first user with a wave in her name. - The error names the column, not the table. When two tables share a column name, check both. Checking one of two feels a lot like checking.
- Reproduce with the customer’s actual input. Her name sat in the ticket for three days while I tested lookalike strings. Pasting the real one ended it in seconds.
- New code is not the default suspect. Day two went into a rollback and a driver upgrade while the culprit predated both.
What I’d Do Different
- One
information_schemaquery across all schemas every quarter, huntingutf8mb3. Takes a minute. - Reproduce tickets with the exact customer input before touching code.
- When a theory dies, write down exactly what I checked. “Checked the charset” is worthless with two tables.
So yeah. Three days, one pointless rollback and one ALTER TABLE, all because a woman put the sea in her name. The emoji survived, and it has earned the right to stay.
