MySQL "Incorrect string value" / emoji needs utf8mb4
MySQL's historic charset named "utf8" only stores three bytes per character. Emoji need four, so inserts die with Error 1366 — or silently become question marks.
utf8 vs utf8mb4 in MySQL
| Charset | Max bytes / character | Stores emoji? | Notes |
|---|---|---|---|
utf8 (alias utf8mb3) | 3 | No | Historic MySQL "utf8". Rejects 4-byte sequences. |
utf8mb4 | 4 | Yes | Real UTF-8. Use this everywhere. |
latin1 | 1 | No | Western European only; causes mojibake with UTF-8 clients. |
Emoji live on Unicode planes above U+FFFF. In UTF-8 they are 4 bytes. A utf8/utf8mb3 column cannot hold them, so MySQL raises:
ERROR 1366 (HY000): Incorrect string value: '\xF0\x9F\x98\x80' for column 'body' at row 1
With non-strict SQL modes some setups store ? instead — permanent data loss. See the crime-scene version in Wirebook's emoji lesson.
Convert existing data
-- Database default for new tables
ALTER DATABASE yourdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- One table (rewrites every text column)
ALTER TABLE your_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Or a single column
ALTER TABLE your_table MODIFY body TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- On MySQL 8+ the default collation is often
utf8mb4_0900_ai_ci; either is fine — pick one and stay consistent. VARCHAR(255)under utf8mb4 may hit the 65535-byte row limit sooner (255×4). IfALTERfails, shorten indexes (e.g. prefix index) or useVARCHAR(191)for unique keys — a classic InnoDB issue.- Backup first.
CONVERT TOrewrites data; it will not resurrect emoji already stored as?.
Connection charset (the silent trap)
Even with utf8mb4 columns, a connection still on utf8 will fail on the way in. Set it every time:
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
-- or in my.cnf / mysqld.cnf
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
ORM / framework notes
| Stack | What to set |
|---|---|
| Django | OPTIONS: {"charset": "utf8mb4"} in DATABASES; older docs mentioning utf8 are wrong for emoji. |
| Rails | encoding: utf8mb4 and collation: utf8mb4_unicode_ci in database.yml; use the utf8mb4 migration helpers. |
| Prisma | URL query ?charset=utf8mb4 (or connection init). Schema @db.VarChar length may need revisiting for unique indexes. |
Node (mysql2 / Sequelize) | charset: 'utf8mb4' in the pool config. Do not use the bare utf8 alias. |
| Python (PyMySQL / SQLAlchemy) | charset=utf8mb4 in the URL or connect args. SQLAlchemy: ?charset=utf8mb4. |
| PHP PDO | PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4", or DSN charset. |
Quick checklist
- Reproduce with a single emoji insert; confirm Error 1366 or
?. SHOW FULL COLUMNS FROM your_table;— look forutf8/utf8mb3.SHOW VARIABLES LIKE 'character_set%';— client/connection/database should all sayutf8mb4.- ALTER + fix the driver/ORM.
- Re-test. For mystery blobs, What is this?; for UTF-8 byte length, Base64. JSON APIs: validate with the JSON formatter (JSON is UTF-8).
Related
Emoji shows as □ / tofu · Mojibake / ðŸ / é · Invisible zero-width characters · Base64 is not encryption · Wirebook emoji lesson · Anvil text X-ray API · UnicodeDecodeError utf-8
Questions
What does MySQL Incorrect string value mean for emoji?
Error 1366 usually means the column or connection is using the old 3-byte utf8 charset. Emoji need utf8mb4.
What is the difference between utf8 and utf8mb4?
In MySQL, utf8 (utf8mb3) stores at most 3 bytes per character and rejects emoji. utf8mb4 is real UTF-8 with a 4-byte maximum.
How do I convert a MySQL table to utf8mb4?
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; also set the connection and ORM charset to utf8mb4.
Why do unique indexes break after switching to utf8mb4?
InnoDB's old 767-byte index limit: VARCHAR(255) utf8mb4 can be 1020 bytes. Shorten the column or use a prefix index (often VARCHAR(191)).
Can I recover emoji already stored as ?
No. The 4-byte sequence was discarded on write. Only mojibake (é / ðŸ) is usually recoverable; see the mojibake guide.
Published .