你正盯著一段紅色的 stack trace:SyntaxError: Unexpected token '<', "..." is not valid JSON。或者是 Unexpected token u in JSON at position 0。無論哪一種,JSON.parse() 都拒絕執行,你的程式碼也停了。本指南說明這個錯誤每一種變體到底是什麼意思、為什麼會發生,以及如何快速修復。
錯誤實際長什麼樣
措辭因 JavaScript 引擎而異,本質都一樣:parser 在字串中某個確切位置撞上了它不預期出現的字元。
// V8 (Chrome, Node.js, Edge)
SyntaxError: Unexpected token '<', "<html>..." is not valid JSON
SyntaxError: Unexpected token 'u', "undefined" is not valid JSON
SyntaxError: Unexpected token u in JSON at position 0 // 較舊的 V8
SyntaxError: Expected ',' or '}' after property value in JSON at position 42
// Firefox (SpiderMonkey)
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data
// Safari (JavaScriptCore)
SyntaxError: JSON Parse error: Unexpected identifier "undefined"
SyntaxError: JSON Parse error: Single quotes (') are not allowed in JSON關鍵資訊總是兩部分:什麼字元不該出現 與 在哪裡。字元告訴你 parser 實際收到了什麼;位置告訴你該去字串的哪裡查看。
原因 1:伺服器回傳 HTML 而不是 JSON
這是 Unexpected token '<' 最常見的成因。你的 fetch 或 AJAX 呼叫拿回了一份 HTML 頁面 —— 通常是 404 頁、登入轉址或伺服器錯誤 —— 而不是你期待的 JSON。那個 < 就是 <!DOCTYPE html> 或 <html> 的開頭標籤。
// ❌ 這會以 "Unexpected token '<'" 崩潰
const res = await fetch('/api/user');
const data = await res.json(); // 伺服器回傳了 HTML 404
// ✅ 先檢查回應狀態
const res = await fetch('/api/user');
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
const data = await res.json(); 呼叫 res.json() 之前一律先檢查 res.ok(或 res.status)。你也可以先看一下原始回應文字:
const text = await res.text();
console.log(text.slice(0, 200)); // 看看你到底拿到了什麼
const data = JSON.parse(text);原因 2:值是 undefined
Unexpected token 'u' 在位置 0 幾乎總是表示你呼叫了 JSON.parse(undefined)。字串 "undefined" 以字母 u 開頭,而 u 不是合法的 JSON 起始字元。
// ❌ 變數從來沒有被賦值
const raw = localStorage.getItem('settings'); // 不存在時回傳 null
JSON.parse(raw); // null 沒問題,但若 raw 不知怎麼變成 undefined...
// ❌ async 資料尚未載入
JSON.parse(this.state.data); // 首次 render 時 data 是 undefined
// ✅ 解析前先 guard
if (raw) {
const data = JSON.parse(raw);
} 注意 localStorage.getItem() 對不存在的 key 回傳的是 null(不是 undefined),而 JSON.parse(null) 會無錯回傳 null。常見元兇是未初始化的 state 變數或缺少的函式引數。
原因 3:用了單引號而非雙引號
// ❌ SyntaxError: Unexpected token '''
JSON.parse("{'name': 'Ada'}");
// ✅ JSON 必須使用雙引號
JSON.parse('{"name": "Ada"}')JavaScript 物件字面值接受單引號。JSON 不行 —— 所有字串(包含 key)都必須用雙引號。常見情境是有人用 Python 的 str() 而不是 json.dumps() 序列化資料,或直接複製一段物件字面值。
原因 4:尾隨逗號
// ❌ SyntaxError: Unexpected token '}'
JSON.parse('{"name": "Ada", "score": 98,}');
// ^ 尾隨逗號
// ✅
JSON.parse('{"name": "Ada", "score": 98}')現代 JavaScript 允許陣列與物件中的尾隨逗號。JSON 不允許。手工編輯的 JSON 設定檔是這個錯誤最常見的來源。
原因 5:沒加引號的 key
// ❌ SyntaxError: Unexpected token 'n'
JSON.parse("{name: 'Ada'}");
// ✅
JSON.parse('{"name": "Ada"}') JSON 裡的物件 key 必須一律是雙引號字串。像 name 這種裸識別字在 JavaScript 物件字面值中合法,在 JSON 中不行。
原因 6:JSON 裡有註解
// ❌ SyntaxError: Unexpected token '/'
JSON.parse(`{
// 使用者紀錄
"name": "Ada"
}`);
// ✅ 解析前先去除註解(或用 JSONC parser)
const stripped = raw.replace(///.*$/gm, '').replace(//*[sS]*?*//g, '');
JSON.parse(stripped);JSON 沒有註解語法。註解常被加進設定檔(這就是 JSONC 格式),但標準的 JSON.parse() 會立刻拒絕。
原因 7:位置 0 有 BOM 或不可見字元
如果錯誤指向位置 0 但第一個可見字元看起來沒問題,可能是字串前面被加上了一個不可見的 byte-order mark(BOM,U+FEFF)或其他控制字元 —— 通常出現在 Windows 上讀取以 UTF-8-BOM 編碼儲存的檔案時。
// ✅ 解析前先移除 BOM
const clean = raw.replace(/^/, '');
JSON.parse(clean);原因 8:token I 或 N —— Infinity 與 NaN
如果錯誤說 Unexpected token I 或 Unexpected token N,那是來源產生了 Infinity、-Infinity 或 NaN —— 它們在 JavaScript 裡都合法,但 在 JSON 裡不合法。
// ❌ 生產端錯誤
const json = '{"ratio": NaN, "limit": Infinity}';
JSON.parse(json); // Unexpected token N (or I) ...
// ✅ 明確地編碼它們
const safe = JSON.stringify({ ratio: NaN, limit: Infinity });
// → '{"ratio":null,"limit":null}' (注意:有損 —— 都變成 null)
// ✅ 無損:編碼成 sentinel 字串,自己再解析回來
JSON.stringify({ ratio: 'NaN', limit: 'Infinity' });JSON.stringify 會悄悄把 NaN / Infinity 轉成 null。若你需要它們可以往返編解碼,輸出時換成 sentinel 字串,輸入時再解析回來。
如何找到錯誤的精確位置
錯誤訊息含有一個 position 數字。用它去切字串,看看那位置到底是什麼:
function parseWithContext(text) {
try {
return JSON.parse(text);
} catch (err) {
// 從 V8 錯誤訊息抽出 position
const match = err.message.match(/position (\d+)/);
if (match) {
const pos = Number(match[1]);
const snippet = text.slice(Math.max(0, pos - 20), pos + 20);
console.error(`Error near: ..."${snippet}"...`);
console.error(` ${''.padStart(Math.min(pos, 20), ' ')}^`);
}
throw err;
}
}常見問題
「Unexpected token in JSON」是什麼意思?
它代表 JSON.parse() 在文法該位置撞到了一個無效字元。錯誤點名的 token 告訴你收到了什麼(< = HTML、u = undefined、' = 單引號),位置告訴你該去哪裡看。
為什麼 fetch 時會出現「Unexpected token '<'」?
你的請求回傳了 HTML 頁面 —— 404、登入轉址或伺服器錯誤 —— 而不是 JSON,那個 < 就是 HTML 開頭標籤。呼叫 response.json() 之前先檢查 response.ok,並 log await response.text() 看看實際回傳了什麼。
怎麼修「Unexpected token u in JSON at position 0」?
你把 undefined 傳給了 JSON.parse()。先 guard 這個值(if (raw) JSON.parse(raw)),並檢查是否有未初始化的 state 或缺少的函式引數。緊密相關的 token o 變體見 Unexpected token o in JSON。
怎麼找出錯誤的精確位置?
從錯誤訊息讀出 position 數字,圍繞它切字串看上下文(見上方程式片段),或將 JSON 貼到 JSON Fix,它會標出問題行。
最快的修法:線上貼上並修復
如果你手上有一段壞掉的 JSON 字串、現在就想把它弄好,JSON Fix 會自動處理上面所有原因 —— 單引號、尾隨逗號、沒引號的 key、註解、Python 字面值 等等。貼進你的 JSON,按 Repair & Format,一步即可拿回乾淨合法的 JSON。一切都在你的瀏覽器中運作,不會送往任何伺服器。
- JSON Fix —— 自動修復壞 JSON 並顯示錯誤行
- 修復 JSON Unexpected Token 錯誤 —— 每種 unexpected token 變體的快速參考
- 修復「[object Object] is Not Valid JSON」 —— 解析前物件被強制轉成字串時引發的相關錯誤
- JSON Diff —— 比對修復前後的 JSON,確認你的修正沒有意外改動