← 全部文章

JSON 第 1 位 Unexpected Token o:原因與修復

那個小寫的「o」是「[object Object]」的第二個字元。你把一個 JavaScript 物件傳給了 JSON.parse() 而不是字串。本文列出此錯誤的每個變體,以及每個的一行修復。

SyntaxError: Unexpected token o in JSON at position 1 —— 那個小寫的 o[object Object] 的第二個字元。它代表你把一個 JavaScript 物件直接傳給了 JSON.parse(),而不是一個 JSON 字串。本文說明這個錯誤的每一種變體,以及如何在一分鐘內修復每一種情況。

為什麼錯誤訊息會說 "token o"

當你把一個非字串值傳給 JSON.parse() 時,JavaScript 會先將它強制轉換為字串。普通物件會被轉換成 "[object Object]"。解析器先看到位置 0 的 [(合法 —— 表示陣列開始),然後是位置 1 的 o(非法 —— 不是合法的陣列元素)。所以會報:Unexpected token o at position 1

JSON.parse({})
// 等同於: JSON.parse("[object Object]")
// SyntaxError: Unexpected token o in JSON at position 1

最常見的原因:沒有 await response.json()

這是實際程式碼庫中這個錯誤的頭號來源。fetch() 回傳的 Response 物件不是字串 —— 它是一個 ReadableStream 的包裝。把它傳給 JSON.parse() 會被強制轉換為 "[object Response]"

// ❌ 錯誤 —— response 是 Response 物件,不是字串
const response = await fetch('/api/user');
const data = JSON.parse(response);
// SyntaxError: Unexpected token o in JSON at position 1

// ✓ 正確 —— 讓瀏覽器把資料流解析為 JSON
const data = await response.json();

同樣地,如果你呼叫 response.text() 但沒有 await,得到的會是一個 Promise 物件(會被強制轉換為 "[object Promise]")。

// ❌ 漏寫了 await —— text 是 Promise,不是字串
const text = response.text();
const data = JSON.parse(text);
// SyntaxError: Unexpected token o in JSON at position 1

// ✓ 正確
const text = await response.text();
const data = JSON.parse(text);

其他會觸發同樣錯誤的物件

你傳入的內容JS 將其強制轉換為錯誤 token
{} 或任何普通物件"[object Object]"token o 位於位置 1
undefined"undefined"token u 位於位置 0
true / false"true" / "false" —— 實際上是合法的 JSON!無錯誤
一個 Response 物件"[object Response]"token o 位於位置 1
一個 Promise"[object Promise]"token o 位於位置 1
null"null" —— 合法 JSON無錯誤(回傳 null)

原因:傳入物件而不是先序列化

一個相關的錯誤:嘗試"解析"你記憶體中已經擁有的 JavaScript 物件。JSON.parse() 接受的是字串;如果你已經有了物件,根本不需要去解析它。

const config = { host: 'localhost', port: 3000 };

// ❌ 毫無意義 —— config 已經是物件
const parsed = JSON.parse(config);

// ✓ 如果想深拷貝,使用 structuredClone 或 stringify + parse
const clone = JSON.parse(JSON.stringify(config));

// ✓ 如果想傳送出去,使用 JSON.stringify
const body = JSON.stringify(config);

原因:Python 或其他後端回傳了錯誤的 Content-Type

有時伺服器回傳的是純文字錯誤訊息(像是 "ok""not found"),狀態碼是 200 而且沒有 Content-Type 標頭。前端預設是 JSON,呼叫 response.json(),於是因為回應內容不是合法 JSON 而拋出 unexpected token 錯誤。

// 伺服器送出的字串字面值: ok
// 瀏覽器嘗試當作 JSON 解析:
JSON.parse("ok")
// SyntaxError: Unexpected token o in JSON at position 0

修復方法:在呼叫 response.json() 之前,先檢查 response.okresponse.headers.get('Content-Type'):

const response = await fetch('/api/action');
if (!response.ok) {
  const text = await response.text();   // 安全 —— 可能不是 JSON
  throw new Error(`HTTP ${response.status}: ${text}`);
}
const data = await response.json();

不太常見的 [object X] 變體

任何 toString() 回傳以 [object 開頭字串的值,都會在位置 1 觸發同樣的錯誤。除了 [object Object] 以外,實際程式碼中真正會遇到的有:

  • [object Module] —— 傳入動態匯入的結果:const mod = await import('./data.json', { with: { type: 'json' } });你想要的是 mod.default,而不是 mod
  • [object AsyncFunction] —— 傳入了非同步函式參考,而不是呼叫它:應序列化 fn()(Promise 的兌現值),而不是 fn
  • [object HTMLDocument] —— 誤傳了 document(例如從除錯器殘留程式碼片段中)。
  • [object FormData] —— 使用 fetch(url, { body: formData }) 沒問題,但 JSON.parse(formData) 不行 —— 請先用 Object.fromEntries(formData) 轉換。

快速修復清單

  • fetch + JSON.parse? 替換為 await response.json()
  • 呼叫鏈上某處漏了 await? 解析之前的每一次 async 呼叫都必須 await。
  • 已經是物件了? 不需要解析 —— 直接使用,或用 structuredClone() 複製。
  • 伺服器出錯時回傳非 JSON? response.text() 讀取,而不是 response.json(),尤其當回應可能不是 JSON 時。

常見問題

"Unexpected token o in JSON at position 1" 是什麼意思?

你把一個 JavaScript 物件傳給了 JSON.parse()。JavaScript 把物件強制轉換為字串 "[object Object]";解析器接受位置 0 的 [,但拒絕位置 1 的 o

使用 fetch 時要如何修復?

不要呼叫 JSON.parse(response)。改用 await response.json(),它會讀取回應資料流並替你解析。確認呼叫鏈中的每一步都有 await。

已經有物件了還需要 JSON.parse 嗎?

不需要。JSON.parse() 只把一個 JSON 字串 轉成值。如果你已經持有物件,直接使用就好,或用 structuredClone() 進行深拷貝,而不要用 JSON.parse(JSON.stringify(obj))

"Unexpected token o" 和 "[object Object] is not valid JSON" 是同一個錯誤嗎?

根本原因相同 —— 物件在解析前被字串化了。較新的 V8 版本會印出 [object Object] is not valid JSON 的措辭;較舊的版本則印出 Unexpected token o。完整說明請見 修復 "[object Object] is not valid JSON"

檢查原始回應

如果不確定 API 實際回傳的內容,把回應內容貼到 JSON Fix 進行驗證。如果不是合法 JSON,驗證工具會明確告訴你哪裡有問題。