← 全部文章

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,校验器会准确告诉你哪里出了问题。