SyntaxError: Bad escaped character in JSON at position N 表示解析器在 JSON 字串裡遇到一個反斜線(\),而後面接的字元不是 JSON 允許的轉義字元。根據 RFC 8259 section 7,JSON 字串只能轉義雙引號、反斜線、正斜線、控制字元轉義 b、f、n、r、t,或者以 u 加上正好 4 位十六進位數字寫成的 Unicode 轉義。
實務除錯時,這個錯誤通常來自某個被複製過來的值:Windows 路徑(C:\Users\Ada)、JavaScript 或 shell 的轉義(\x1b)、正則模式(\d+)、Python 風格的 Unicode 轉義(\U0001F600),或者一個從 log 裡半解碼過的字串。修復不是「把反斜線刪掉」,而是先決定最終字串的值應該是什麼,然後寫出能代表那個值的 JSON 文字。
這篇指南以 JavaScript JSON.parse() 的錯誤訊息為主,但同一條規則也適用於 Python json.loads()、Go encoding/json、Ruby JSON.parse、PHP json_decode、jq、Postgres jsonb,以及大多數嚴格的 JSON 解析器。
我遇到的是哪一種字串錯誤?
- Bad escaped character:一個
\後面接了 JSON 不允許的字元,例如\x、\d、\',或\Users。- Bad control character:字串裡出現原始的 tab、換行、NUL 位元組,或 ANSI ESC 位元組。
- Unterminated string:字串以
"開頭卻沒有結束。
30 秒修復法
- 跳到錯誤訊息回報的
position、line或column。 - 往前看一個字元是否為反斜線。
- 檢查反斜線後面接的字元。
- 如果那個反斜線本身是資料的一部分,寫成
\\。 - 如果那是別種語言的轉義(
\x、\d、\U),把它翻譯成 JSON 語法。 - 如果那個反斜線只是從被引號包起來的 log 裡複製出來的,就一層一層地解析,不要用正則去剝除。
範例:
{"path":"C:\Users\Ada\file.json"}
^
U is not valid after a JSON backslash
正確的 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 為例,被回報的字元是 \Users 裡的 U:
{"path":"C:\Users\Ada\file.json"}
^^
\U is the bad escape
所以當訊息說 position 12 時,記得檢查 position 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 位十六進位數字 |
其他一律不是合法的 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" } |
這個不會失敗。正斜線不需要轉義。 | 如果接收方接受正斜線,直接保留即可。 |
{ "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 位十六進位。 |
{ "char": "\u0012" } |
{ "emoji": "\u{1F600}" } |
JavaScript 原始碼字串支援這種寫法,JSON 不支援。 | { "emoji": "😀" } 或 { "emoji": "\uD83D\uDE00" } |
有一個細節值得單獨提醒:\f 是合法的 JSON 轉義。若一個 Windows 路徑裡包含 \file,解析器可能把它變成一個換頁字元後面接著 ile。解析會成功,但路徑的值已經被破壞了。這就是為什麼盲目「修復」路徑字串很危險。
原因 1:Windows 路徑被複製進 JSON
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 文字混在一起
這是網路上很多範例會搞混人的地方。這裡有兩層:
- 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 文字。
讀 stack trace 時可以用這個心智模型:如果 JSON 來自 .json 檔案、HTTP body、localStorage 的值,或資料庫字串,就修 JSON 文字。如果 JSON 是寫在 JavaScript 原始碼的字串裡,你可能得同時處理一層 JavaScript 的轉義、再處理一層 JSON 的轉義。
原因 3:借用其他語言的轉義
JSON 接受 \n 和 \t,但很多在其他程式語言裡很常見的轉義它並不接受:
{ "code": "\x1b[0m", "name": "O\'Brien" }
合法的 JSON:
{
"code": "\u001b[0m",
"name": "O'Brien"
}
常見的假朋友:
| 轉義 | 在哪些語言合法 | JSON 的正確寫法 |
|---|---|---|
\x1b |
JavaScript、Python、多數 shell | \u001b |
\' |
JavaScript/Python 的單引號字串 | 直接寫 ',前面不加反斜線 |
\0 |
JavaScript/Python 的 NUL 簡寫 | \u0000 |
\v |
JavaScript 的垂直定位 | \u000b |
\U0001F600 |
Python 的 Unicode 轉義 | 直接用 UTF-8 的 emoji 字面值,或代理對 |
\u{1F600} |
JavaScript 的 Unicode 碼點轉義 | 直接用 UTF-8 的 emoji 字面值,或代理對 |
如果 JSON 是你自己的程式產生的,不要一個一個手工翻譯,改成建立一個正常的物件,讓該語言的 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、命名群組、lookbehind 範例,以及替換字串。只要反斜線是留給後面的解析器用的,在 JSON 裡就要把它加倍。
原因 5:畸形的 Unicode 轉義
JSON 的 Unicode 轉義是固定寬度:
{ "char": "\u12" }
合法的 JSON:
{
"char": "\u0012"
}
u 必須小寫,後面正好接 4 位十六進位數字:0-9、a-f 或 A-F。
以下這些都不是 JSON 的 Unicode 轉義:
"\u{2028}" // JavaScript 原始碼風格,不是 JSON
"\U00002028" // Python 風格,不是 JSON
"\u20G0" // G 不是十六進位數字
BMP(基本多文種平面)以外的字元,例如許多 emoji 和某些數學符號,可以直接以 UTF-8 字面值存進 JSON:
{
"emoji": "😀"
}
要用轉義的話,得寫成 UTF-16 的代理對:
{
"emoji": "\uD83D\uDE00"
}
要避免孤立的代理,例如只寫 \uD83D 而沒有配對的低位代理。有些解析器會把它們當成碼元接受,但下游若要求格式良好的 Unicode 就可能拒收。
原因 6:手工拼接 JSON 字串
這是這個 bug 的正式生產版:
// Unsafe: userInput may contain backslashes, quotes, or newlines.
const payload = '{"message":"' + userInput + '"}';
如果 userInput 是 C:\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 風格帶 line 和 column 的錯誤,先跳到那一行,然後檢查該行的字串字面值。如果精確的欄位落在反斜線之後,把前一個字元一起讀進來看。
用修復工具還是拒收 payload?
以下情境使用修復工具:
- 你在整理貼進來的片段。
- 你在除錯一行 log。
- 你在檢視 LLM 的輸出。
- 你能用肉眼確認修復後的值是對的。
- 這個值不會用於金流、權限、刪除或不可逆的狀態變更。
以下情境要拒收 payload,回頭修生產者端:
- JSON 來自 API 契約。
- 這個值會影響計費、權限、安全或資料刪除。
- 解析器必須在多種可能的意義之間猜測。
- 一個路徑、正則或轉義序列可能語法合法但語意錯誤。
例如,修復 C:\Users\Ada\file.json 不只是語法操作。\file 裡的 \f 是合法的轉義,因此工具可能會把它解析成一個換頁字元,而不是保留反斜線。這時得由人或生產者端的程式決定原本的路徑到底是什麼。
本站的 JSON Fix 工具最適合當作瀏覽器本地的除錯助手:貼上文字、檢查輸出,再驗證修復後的 JSON。它不應該成為靜默吞下畸形生產 payload 的中介層。
如何安全地反轉義 JSON
有時候反斜線本身沒錯,只是 JSON 被雙重編碼了。你可能在 log 裡看到這個:
{\"name\":\"Ada\",\"path\":\"C:\\\\Users\\\\Ada\"}
不要一口氣執行 replace(/\\/g, '')。那會把真正的轉義破壞掉。
一次解析一層合法的 JSON:
// The outer value is a JSON string that contains JSON text.
const wrapped = '"{\\"name\\":\\"Ada\\",\\"path\\":\\"C:\\\\\\\\Users\\\\\\\\Ada\\"}"';
const once = JSON.parse(wrapped);
// once is: {"name":"Ada","path":"C:\\Users\\Ada"}
const data = JSON.parse(once);
// data is: { name: "Ada", path: "C:\\Users\\Ada" }
如果第一次解析就以 Bad escaped character 失敗,那輸入就不只是被編碼過,而是無效的 JSON 文字,需要針對性的修復。
預防清單
- 絕不要把使用者字串串接進 JSON。
- 使用
JSON.stringify()、json.dumps()、json.Marshal(),或你所在平台的 JSON 序列化器。 - 在 JSON 裡存正則模式時要把反斜線加倍。
- 若接收端接受,路徑優先使用正斜線。
- 從 log、shell、文件複製過來的範例要先加引號並測試。
- 在 CI 用真正的解析器驗證產出的
.json檔案。 - 記錄一段解析器位置附近的安全預覽,而不是把整個 payload 丟到 log。
- 把自動修復當成開發流程,而不是生產契約。
常見問題
「Bad escaped character in JSON」是什麼意思?
JSON 字串中的反斜線後面接了 JSON 不允許出現在 \ 之後的字元。合法的轉義是 "、\、/、b、f、n、r、t,和 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 和 shell 範例裡很常見,但 JSON 不支援。ANSI ESC 字元請用 \u001b,或者在序列化 log 前先把 ANSI 顏色碼去掉。
這跟「Bad control character」是同一回事嗎?
不是。「Bad escaped character」表示反斜線後面那個字元不合法。「Bad control character」表示 JSON 字串裡出現了原始控制位元組,例如字面上的換行、tab、NUL 或 ESC。
JSON 修復工具能自動修好壞掉的轉義嗎?
有時候可以,例如貼進來的片段,且原本的值一看就明白。不要對 API payload、資安相關資料、付款、權限、刪除,或那些 \f、\n、\t 可能合法但並非本意的值,做靜默的自動修復。
怎麼反轉義 JSON?
用 JSON.parse() 一次解一層。一個被雙重編碼的值,第一次 parse 後會變成正常的 JSON 字串,第二次 parse 後才會變成真正的物件或陣列。不要用正則去剝反斜線,那會破壞合法的轉義。
怎麼在原始碼裡預防這個錯誤?
建立原生的值,再用 JSON.stringify() 或你所在語言對應的序列化器序列化它們。不要用字串串接去組 JSON。
立刻修復
- JSON Fix —— 在瀏覽器裡定位並修復無效的轉義。
- JSON Stringify —— 轉義與反轉義 JSON 字串字面值。
- Escape JSON as a String Literal —— 處理巢狀與雙重編碼的 JSON。
- Bad Control Character in JSON —— 原始控制位元組與轉義文字的差別。
- Unterminated String in JSON —— JSON 字串沒結束的情況。
- 如何修復 JSON.parse 的「Unexpected Token」錯誤 —— 更完整的 JSON 解析錯誤指南。
參考資料
- RFC 8259 section 7 —— JSON 字串文法與完整的轉義清單。
- MDN JSON.parse —— JavaScript 解析器的行為與
SyntaxError處理。 - MDN JSON.stringify —— 從 JavaScript 值安全產生 JSON。
- MDN JSON.parse bad parsing errors —— 常見的瀏覽器 JSON parse 錯誤訊息。
- MDN String and UTF-16 —— UTF-16 碼元、代理對,以及格式良好的字串。
最後審閱於 2026 年 7 月。