← 記事一覧

JSON の不正なエスケープ文字: 原因、例、修正方法

\x エスケープ、Windows パス、正規表現文字列、不正な \u 値、二重エンコード JSON による「bad escaped character in JSON」エラーを修正します。

SyntaxError: Bad escaped character in JSON at position N は、パーサが JSON 文字列の中にバックスラッシュ(\)を見つけ、その直後の文字が JSON で許されているエスケープ文字ではなかったことを意味します。RFC 8259 セクション 7 によれば、JSON 文字列でエスケープできるのは、ダブルクォート、バックスラッシュ、スラッシュ、制御エスケープの bfnrt、または u の後にちょうど 4 桁の 16 進数を続けて書く Unicode エスケープだけです。

実際のデバッグでは、このエラーは通常、コピーされた 1 つの値から発生します。Windows パス(C:\Users\Ada)、JavaScript やシェルのエスケープ(\x1b)、正規表現パターン(\d+)、Python 形式の Unicode エスケープ(\U0001F600)、あるいはログから半分だけアンエスケープされた文字列などです。修正方法は「バックスラッシュを削除する」ことではありません。修正方法は、最終的な文字列値がどうあるべきかを決め、その値を表現する JSON テキストを書くことです。

このガイドは JavaScript の JSON.parse() の文言を中心に扱いますが、同じルールが Python の json.loads()、Go の encoding/json、Ruby の JSON.parse、PHP の json_decode、jq、Postgres の jsonb、その他の厳密な JSON パーサにも当てはまります。

どの文字列エラーですか?

  • Bad escaped character: \ の直後に \x\d\'\Users など JSON が許さない文字が続いている。
  • Bad control character: 生のタブ、改行、NUL バイト、ANSI ESC バイトが文字列の中に含まれている。
  • Unterminated string: 文字列が " で始まったが閉じられていない。

30 秒でできる修正

  1. 報告された positionlinecolumn の位置に移動します。
  2. その 1 文字前にバックスラッシュがないか確認します。
  3. バックスラッシュの後の文字を確認します。
  4. バックスラッシュがデータの一部なら、\\ と書きます。
  5. エスケープが他言語のもの(\x\d\U)なら、JSON の構文に変換します。
  6. バックスラッシュが引用されたログ行からコピーされただけなら、正規表現で剥がすのではなく、1 層だけパースします。

例:

{"path":"C:\Users\Ada\file.json"}
           ^
           U は JSON のバックスラッシュの後に有効ではない

正しい JSON テキスト:

{
  "path": "C:\\Users\\Ada\\file.json"
}

パース後、アプリケーションが実際に扱う値は次のままです:

C:\Users\Ada\file.json

倍になったバックスラッシュは、JSON テキスト上にしか存在しません。

エラーの見え方

エンジンによって表現は少し異なります:

// V8: Chrome, Node.js, Edge
SyntaxError: Bad escaped character in JSON at position 12

// Firefox
SyntaxError: JSON.parse: bad escaped character at line 1 column 13 of the JSON data

// Safari
SyntaxError: JSON Parse error: Invalid escape character \x

V8 の position は、通常バックスラッシュそのものではなく、その直後の文字を指しています。次の壊れた JSON では、報告される文字は \UsersU です:

{"path":"C:\Users\Ada\file.json"}
           ^^
           \U が不正なエスケープ

そのため、メッセージに position 12 と出たら、位置 12 の前後の小さな範囲を調べてください。指し示された文字も役立ちますが、その手前のバックスラッシュがバグを説明しています。

JSON が許す唯一のエスケープ

JSON 文字列の中で、バックスラッシュが導入できるのは次のエスケープだけです:

JSON エスケープ パース後の文字 備考
\" " JSON 文字列の中にダブルクォートを入れるために必要
\\ \ リテラルなバックスラッシュに必要
\/ / 任意;/ はエスケープなしでも有効
\b バックスペース U+0008
\f フォームフィード U+000C;\file が Windows パスで危険なのはこのため
\n 改行 U+000A
\r 復帰 U+000D
\t タブ U+0009
\uXXXX Unicode コードユニット 小文字の u の後にちょうど 4 桁の 16 進数

これら以外はすべて JSON として無効です:\x\'\d\s\w\0\v\e\U\u{1F600}\N{...}\cA、そして \u12 のような短い Unicode エスケープなど。

早見表

最終的な値がすでに分かっている場合は、この表を使ってください。

壊れた JSON テキスト 失敗する理由 有効な JSON テキスト
{ "path": "C:\Users\Ada\file.json" } \U\A は無効;\f は有効だがフォームフィードになり、パス区切りにはならない。 { "path": "C:\\Users\\Ada\\file.json" }
{ "path": "C:/Users/Ada/file.json" } 失敗しません。スラッシュはエスケープ不要です。 受信側がスラッシュを受け付けるならそのままで OK。
{ "color": "\x1b[32mOK\x1b[0m" } JSON には \xNN エスケープがありません。 { "color": "\u001b[32mOK\u001b[0m" }
{ "name": "O\'Brien" } JSON 文字列でアポストロフィはエスケープ不要です。 { "name": "O'Brien" }
{ "pattern": "^\d{4}-\d{2}-\d{2}$" } \d は正規表現のエスケープであり、JSON のエスケープではありません。 { "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }
{ "char": "\u12" } \u の後にはちょうど 4 桁の 16 進数が必要です。 { "char": "\u0012" }
{ "emoji": "\u{1F600}" } JavaScript のソース文字列ではサポートされますが、JSON ではされません。 { "emoji": "😀" } または { "emoji": "\uD83D\uDE00" }

厄介な注意点が一つあります:\f は JSON で有効なエスケープです。Windows パスに \file が含まれていると、パーサはそれをフォームフィード文字とそれに続く ile に変換してしまいます。パスの値が破損したままパースが成功することがあります。パス文字列を無闇に「修復」するのが危険なのはこのためです。

原因 1: JSON にコピーされた Windows パス

Windows パスは、人間にはバックスラッシュがパスの区切りに見えるため、無害に見えます:

{ "downloadDir": "C:\Users\Ada\Downloads" }

JSON はバックスラッシュをエスケープシーケンスの開始として読みます。\U を見つけたところで、大文字の U は JSON のエスケープではないので停止します。

JSON では、バックスラッシュを二重にして書きます:

{
  "downloadDir": "C:\\Users\\Ada\\Downloads"
}

または、受信側のプログラムが受け付けるならスラッシュを使います:

{
  "downloadDir": "C:/Users/Ada/Downloads"
}

設定ファイルではスラッシュのほうがミスが少ないことが多いです。Windows 限定の厳密な値には、二重バックスラッシュが移植性のある JSON 表現になります。

原因 2: JavaScript ソース文字列と JSON テキストの混同

ここは、ウェブ上の多くの例がうっかり読者を混乱させる場所です。層は 2 つあります:

  • JavaScript のソース文字列構文
  • その JavaScript 文字列の中に入っている JSON テキスト構文

次の JavaScript ソースコードは有効です:

const raw = '{"path":"C:\\Users\\Ada"}';
JSON.parse(raw);

しかし、実際にパーサに渡る JSON テキストは次のとおりです:

{"path":"C:\\Users\\Ada"}

JavaScript にバックスラッシュを先に食べさせずに、壊れた JSON サンプルを JavaScript でテストしたいときは、String.raw を使います:

const broken = String.raw`{"path":"C:\Users\Ada"}`;
JSON.parse(broken);

これは Bad escaped character を投げます。なぜなら JSON.parse() が実際の壊れた JSON テキストを受け取るからです。

スタックトレースを読むときは、次のメンタルモデルを使ってください:JSON が .json ファイル、HTTP ボディ、localStorage の値、データベースの文字列から来ているなら、JSON テキストを直します。JSON が JavaScript のソース文字列の中にあるなら、JavaScript 用のエスケープと JSON 用のエスケープが 1 段階ずつ必要になるかもしれません。

原因 3: 他言語からエスケープを借用する

JSON は \n\t を受け付けますが、プログラミング言語で普通のエスケープでも受け付けないものが多数あります:

{ "code": "\x1b[0m", "name": "O\'Brien" }

有効な JSON:

{
  "code": "\u001b[0m",
  "name": "O'Brien"
}

よくある偽の友達:

エスケープ 有効な場所 JSON での修正
\x1b JavaScript、Python、多くのシェル \u001b
\' JavaScript/Python のシングルクォート文字列 バックスラッシュなしで ' を使う
\0 JavaScript/Python の NUL の略記 \u0000
\v JavaScript の垂直タブ \u000b
\U0001F600 Python の Unicode エスケープ UTF-8 の絵文字リテラル、またはサロゲートペア
\u{1F600} JavaScript の Unicode コードポイントエスケープ UTF-8 の絵文字リテラル、またはサロゲートペア

生成側が自分のコードなら、ケースごとに手作業で変換しないでください。普通のオブジェクトを組み立て、その言語の JSON シリアライザに有効な JSON を書かせてください。

原因 4: JSON 設定に保存された正規表現パターン

正規表現には独自のエスケープ言語があります。JSON 文字列にも別のエスケープ言語があります。正規表現のバックスラッシュは、正規表現エンジンに届く前に JSON パースを生き延びなければなりません。

壊れた JSON 設定:

{ "datePattern": "^\d{4}-\d{2}-\d{2}$" }

有効な JSON 設定:

{
  "datePattern": "^\\d{4}-\\d{2}-\\d{2}$"
}

JSON パースの後、アプリケーションは次の文字列を見ます:

^\d{4}-\d{2}-\d{2}$

そこで初めて、これを正規表現にすべきです:

const config = JSON.parse('{"datePattern":"^\\\\d{4}-\\\\d{2}-\\\\d{2}$"}');
const re = new RegExp(config.datePattern);

同じルールが \s\w\b、名前付きグループ、後読みの例、置換文字列にも当てはまります。バックスラッシュが後段のパーサに向けたものなら、JSON で二重にしてください。

原因 5: 不正な形式の Unicode エスケープ

JSON の Unicode エスケープは固定長です:

{ "char": "\u12" }

有効な JSON:

{
  "char": "\u0012"
}

u は小文字でなければならず、その後にちょうど 4 桁の 16 進数(0-9a-fA-F)が続きます。

これらは JSON の Unicode エスケープではありません:

"\u{2028}"   // JavaScript のソース形式、JSON ではない
"\U00002028" // Python 形式、JSON ではない
"\u20G0"     // G は 16 進数ではない

基本多言語面(BMP)の外にある文字、たとえば多くの絵文字や一部の数学記号は、UTF-8 の JSON にリテラルとして保存できます:

{
  "emoji": "😀"
}

エスケープする場合は、UTF-16 のサロゲートペアで表現します:

{
  "emoji": "\uD83D\uDE00"
}

対応する低位サロゲートのない \uD83D のような孤立サロゲートは避けてください。パーサによってはコードユニットとして受け付けますが、整形式 Unicode を要求する下流のシステムは拒否することがあります。

原因 6: 手作りの JSON 文字列

これはこのバグの本番版です:

// 危険: userInput にはバックスラッシュ、クォート、改行が含まれるかもしれない。
const payload = '{"message":"' + userInput + '"}';

userInputC:\Users\Ada なら、出力されるテキストは無効な JSON です。" が含まれていれば、JSON は別の形で壊れます。生の改行が含まれていれば、代わりに bad control character エラーになることがあります。

シリアライザを使ってください:

const payload = JSON.stringify({
  message: userInput,
  path: 'C:\\Users\\Ada\\file.json',
  code: '\x1b[32mOK\x1b[0m',
});

JSON.stringify() は JSON 固有のエスケープを処理します。結果は有効な JSON テキストです:

{
  "message": "...",
  "path": "C:\\Users\\Ada\\file.json",
  "code": "\u001b[32mOK\u001b[0m"
}

他の言語でも同じ原則が当てはまります:

import json

payload = json.dumps({
    "path": r"C:\Users\Ada\file.json",
    "pattern": r"^\d+$",
})
body, err := json.Marshal(map[string]string{
    "path": `C:\Users\Ada\file.json`,
    "pattern": `^\d+$`,
})

生成側を直しているなら、これが本当の修正です。無効な JSON を下流でパッチするのは、不正なテキストを作った場所を隠すだけです。

不正なエスケープの位置の特定方法

貼り付けた JSON では、次の小さなヘルパーで V8 の position 周辺が見やすくなります:

function showJsonParseContext(raw) {
  try {
    JSON.parse(raw);
    console.log('Valid JSON');
  } catch (error) {
    const message = String(error.message);
    const match = message.match(/position (\d+)/);

    if (!match) {
      console.log(message);
      return;
    }

    const pos = Number(match[1]);
    const start = Math.max(0, pos - 24);
    const end = Math.min(raw.length, pos + 24);
    const excerpt = raw.slice(start, end);

    console.log(message);
    console.log(JSON.stringify(excerpt));
    console.log(' '.repeat(pos - start) + '^');
  }
}

const raw = String.raw`{"path":"C:\Users\Ada\file.json"}`;
showJsonParseContext(raw);

JSON.stringify(excerpt) はわざとです。バックスラッシュや制御文字を目に見えるエスケープとして表示するので、バグが不可視の空白や行き過ぎたエスケープの場合にちょうど必要なものが得られます。

Firefox 形式の linecolumn エラーでは、まずその行にジャンプし、次にその行の文字列リテラルを調べてください。ちょうどそのカラムがバックスラッシュの後ろに落ちるなら、その前の文字も読んでください。

修復ツールを使うか、それともペイロードを拒否するか?

修復ツールを使うのはこんなときです:

  • 貼り付けたスニペットをクリーンアップしている。
  • ログ行をデバッグしている。
  • LLM の出力をレビューしている。
  • 修復された値を目視で確認できる。
  • その値が金銭の移動、権限、削除、不可逆な状態変更を駆動していない。

ペイロードを拒否して生成側を直すのはこんなときです:

  • JSON が API 契約から来ている。
  • 値が請求、権限、セキュリティ、データ削除に影響する。
  • パーサが複数の解釈候補から推測しなければならなかった。
  • パス、正規表現、エスケープシーケンスが有効ではあっても意味的には間違っている可能性がある。

たとえば、C:\Users\Ada\file.json の修復は単なる構文操作ではありません。\file の中の \f は有効なエスケープなので、ツールはバックスラッシュを保存する代わりにフォームフィード文字としてパースするかもしれません。意図されたパスは、人間か生成側のコードが決めるべきです。

このサイトの JSON Fix ツールは、ブラウザ内のデバッグアシスタントとして使うのが最適です。テキストを貼り付け、出力を確認し、修復後の JSON を検証してください。壊れた本番ペイロードの黙示的な取り込み層にすべきではありません。

JSON を安全にアンエスケープする方法

バックスラッシュが悪いのではなく、JSON が二重エンコードされているだけの場合もあります。ログでこう見えるかもしれません:

{\"name\":\"Ada\",\"path\":\"C:\\\\Users\\\\Ada\"}

無差別な replace(/\\/g, '') は実行しないでください。本当のエスケープを破壊します。

有効な JSON の層を 1 つずつパースしてください:

// 外側の値は JSON テキストを含む JSON 文字列。
const wrapped = '"{\\"name\\":\\"Ada\\",\\"path\\":\\"C:\\\\\\\\Users\\\\\\\\Ada\\"}"';

const once = JSON.parse(wrapped);
// once は: {"name":"Ada","path":"C:\\Users\\Ada"}

const data = JSON.parse(once);
// data は: { name: "Ada", path: "C:\\Users\\Ada" }

最初のパースが Bad escaped character で失敗するなら、入力は単にエンコードされているのではなく、無効な JSON テキストであり、狙いを定めた修復が必要です。

予防チェックリスト

  • ユーザー文字列を JSON に連結しないでください。
  • JSON.stringify()json.dumps()json.Marshal()、あるいはお使いのプラットフォームの JSON シリアライザを使ってください。
  • 正規表現パターンを JSON に保存するときはバックスラッシュを二重にしてください。
  • 受信側がスラッシュを受け付けるなら、パスにはスラッシュを優先してください。
  • ログ、シェル、ドキュメントからコピーした例は引用してテストしてください。
  • 生成された .json ファイルは、CI で本物のパーサを使って検証してください。
  • ペイロード全体をログに残す代わりに、パーサ位置周辺の安全なプレビューをログに出してください。
  • 自動修復は本番の契約としてではなく、開発者ワークフローとして扱ってください。

よくある質問

「Bad escaped character in JSON」とはどういう意味ですか?

JSON 文字列の中にあるバックスラッシュの後に、JSON が \ の後に許さない文字が続いています。有効なエスケープは "\/bfnrt、そして uXXXX です。

JSON の中の Windows パスはどう直しますか?

パスのバックスラッシュはすべて \\ として書きます。たとえば C:\\Users\\Ada\\file.json です。受信側のプログラムがスラッシュを受け付けるなら、C:/Users/Ada/file.json は有効な JSON で、読みやすくもなります。

なぜ正規表現は JavaScript では動くのに JSON では失敗するのですか?

JSON パーサは、正規表現エンジンよりも先に文字列を見ます。\d のような正規表現エスケープは JSON では \\d と書かなければならず、そうすればパース後の文字列が \d を含んだままになります。

\x1b は有効な JSON ですか?

いいえ。\xNN は JavaScript、Python、シェルの例では一般的ですが、JSON はサポートしません。ANSI ESC 文字には \u001b を使うか、ログをシリアライズする前に ANSI カラーコードを削除してください。

これは「Bad control character」と同じですか?

いいえ。「Bad escaped character」はバックスラッシュの後の文字が無効であることを意味します。「Bad control character」は、生の制御バイト、たとえば改行、タブ、NUL、ESC バイトなどが JSON 文字列の中にリテラルとして現れていることを意味します。

JSON 修復ツールは不正なエスケープを自動的に直せますか?

意図された値が明白な、貼り付けたスニペットに対しては可能なこともあります。API ペイロード、セキュリティ機密データ、支払い、権限、削除、あるいは \f\n\t が有効でも意図とは異なる可能性がある値については、静かに自動修復しないでください。

JSON はどうアンエスケープしますか?

JSON.parse() で 1 層ずつパースしてください。二重エンコードされた値は、最初のパースの後は普通の JSON 文字列に、2 回目のパースの後は本物のオブジェクトや配列になります。有効なエスケープを破壊するので、正規表現でバックスラッシュを剥がすことは避けてください。

ソースコードでこのエラーを予防するにはどうしますか?

ネイティブの値を組み立て、JSON.stringify() またはあなたの言語の同等のシリアライザでシリアライズしてください。文字列連結で JSON を組み立てないでください。

今すぐ直す

Sources

最終レビュー: 2026 年 7 月。