できない.dev

MySQL で「Incorrect string value」が出て絵文字を保存できない

Incorrect string value は、テーブル・カラム・接続の文字コードを utf8mb4 に揃えれば直る。
絵文字は 4 バイト必要で、utf8mb3 では格納できない。
どれか一つでも取り残すと再発する。

公開: 更新:

症状できない · exit 1
mysql -u root --default-character-set=utf8mb4 -v app_db < bad.sql
--------------
INSERT INTO posts (body) VALUES ('寿司 🍣')
--------------

ERROR 1366 (HY000) at line 1: Incorrect string value: '\xF0\x9F\x8D\xA3' for column 'body' at row 1
解決後できた · exit 0
mysql -u root --default-character-set=utf8mb4 -t -v app_db < retry.sql
…
| id | body        | chars | bytes |
+----+-------------+-------+-------+
|  1 | 寿司 🍣       |     4 |    11 |
+----+-------------+-------+-------+

2026-10-10 に mysql:8.4(MySQL 8.4.11) で実際に打って取った出力。検証環境の詳細

要約

Incorrect string value: '\xF0\x9F...' for column ... は、4 バイトの文字を 4 バイト格納できない文字コードのカラムへ入れようとしたときに出る。

絵文字や一部の漢字は UTF-8 で 4 バイトを要する。
MySQL の utf8mb4 は 4 バイトを扱えるが、utf8mb3 は 3 バイトまでで、そもそも格納できない。
テーブル・カラム・接続を utf8mb4 に揃えるのが解決策である。

実行例

mysql:8.4 で CHARACTER SET utf8 を指定して作ったテーブルは SHOW CREATE TABLE で utf8mb3 と表示され、接続を utf8mb4 にそろえても絵文字の INSERT は ERROR 1366 (HY000) の Incorrect string value で失敗した。CONVERT TO CHARACTER SET utf8mb4 で変換した後は同じ INSERT が通り、絵文字 1 文字が 4 バイトで格納されている。

$ mysql -u root --default-character-set=utf8mb4 -v app_db < bad.sql
--------------
INSERT INTO posts (body) VALUES ('寿司 🍣')
--------------
 
ERROR 1366 (HY000) at line 1: Incorrect string value: '\xF0\x9F\x8D\xA3' for column 'body' at row 1
$ echo $?
1
$ mysql -u root --default-character-set=utf8mb4 -t app_db < show.sql
*************************** 1. row ***************************
       Table: posts
Create Table: CREATE TABLE `posts` (
  `id` int NOT NULL AUTO_INCREMENT,
  `body` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
+--------------------------+---------+
| Variable_name            | Value   |
+--------------------------+---------+
| character_set_connection | utf8mb4 |
+--------------------------+---------+
$ mysql -u root --default-character-set=utf8mb4 -t -v app_db < solve_alter.sql
--------------
ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci
--------------
 
--------------
SELECT column_name, character_set_name
  FROM information_schema.columns
 WHERE table_schema = 'app_db' AND table_name = 'posts' AND column_name = 'body'
--------------
 
+-------------+--------------------+
| COLUMN_NAME | CHARACTER_SET_NAME |
+-------------+--------------------+
| body        | utf8mb4            |
+-------------+--------------------+
$ mysql -u root --default-character-set=utf8mb4 -t -v app_db < retry.sql
--------------
INSERT INTO posts (body) VALUES ('寿司 🍣')
--------------
 
--------------
SELECT id, body, CHAR_LENGTH(body) AS chars, LENGTH(body) AS bytes FROM posts
--------------
 
+----+-------------+-------+-------+
| id | body        | chars | bytes |
+----+-------------+-------+-------+
|  1 | 寿司 🍣       |     4 |    11 |
+----+-------------+-------+-------+
$ echo $?
0

— 2026-10-10 時点の出力

検証環境MySQL 8.4.11 / 2026-10-10 検証
検証日
実行環境
mysql:8.4 Oracle Linux Server 9.8
バージョン
  • MySQL 8.4.11

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

解決策

1. 現状の文字コードを確認する

SHOW CREATE TABLE posts;
SHOW VARIABLES LIKE 'character_set%';

テーブル定義と接続の両方を見る。
片方だけ直しても再発する。

2. テーブルを utf8mb4 に変換する

ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

CONVERT TO CHARACTER SET はテーブル内の文字列カラムをまとめて変換する。
MySQL 8.0 の既定の照合順序は utf8mb4_0900_ai_ci である(公式ドキュメント(新しいタブで開く))。

3. データベース既定を変える

ALTER DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

以降に作るテーブルが utf8mb4 になる。
既存テーブルは変わらないので、個別に変換する。

4. 接続の文字コードを合わせる

SET NAMES utf8mb4;

アプリからは接続文字列やドライバの設定で指定するのが確実である。
Node.js の mysql2 なら charset: "utf8mb4"、Python の PyMySQL なら charset="utf8mb4" を渡す。

5. 桁あふれに注意する

utf8mb4 は 1 文字あたり最大 4 バイトを使うため、インデックスの長さ制限に当たることがある。
その場合は対象カラムの長さを見直す。

よくある原因

  1. カラムが utf8mb3: 古い定義のまま作られたテーブルへ絵文字を入れようとしている。
  2. 接続が utf8mb4 でない: 保存先が正しくても、接続の文字コードが違えば送信時点で壊れる。
  3. データベース既定が古い: 新しく作るテーブルが自動的に utf8mb3 になる。
  4. 別名 utf8 の指定: utf8 は utf8mb3 の非推奨な別名で、指定しても 4 バイトは扱えない。

この手順で直った?