SyntaxError: Unexpected end of JSON input 表示解析器在 JSON 结构还没结束前就读到了字符串末尾。数据被截断了。本文展示这种情况发生的所有常见方式,以及每一种该怎么修。
这个错误是什么意思
JSON.parse() 是一个状态机。它一次一个字符地读,追踪打开的方括号、花括号和字符串分隔符。当它走到输入末尾,但还「在某个东西里面」 —— 对象、数组或字符串 —— 它就会抛:
SyntaxError: Unexpected end of JSON input这个错误没有行号、没有位置,因为问题本身就是内容缺失 —— 没有东西可以指。
原因 1 —— API 响应被截断
这是真实世界里最常见的原因。服务器发了一个大的 JSON body,要么网络早早断了连接、要么响应还没读完就被读了、要么代理或 CDN 的缓冲限制被触发了。
// 实际收到的(响应到一半连接掉了):
'{"users":[{"id":1,"name":"Ada"},{"id":2,"na'
JSON.parse('{"users":[{"id":1,...') // SyntaxError: Unexpected end of JSON input修复: 检查 HTTP 状态码和 Content-Length header。解析前先把原始响应的长度打到日志里。如果用 fetch,总是 await response.json(),而不是先读 response.text() 再自己解析 —— 浏览器内建的 JSON 解析器对截断会给出更清晰的错误。
// 正确
const data = await response.json();
// 脆弱 —— 把截断错误藏起来了
const text = await response.text();
const data = JSON.parse(text);原因 2 —— 对象或数组没闭合
JSON 是手写的,或者由代码生成时少写了一个收尾的方括号或花括号。
JSON.parse('{"name":"Ada","plan":"pro"')
// 少了收尾的 }
// SyntaxError: Unexpected end of JSON input
JSON.parse('[1, 2, 3')
// 少了收尾的 ]
// SyntaxError: Unexpected end of JSON input修复: 用 JSON linter,或者把字符串贴到 JSON Fix —— 它会找出没闭合的那个分隔符,并且在宽松模式下能自动把缺的括号补上。
原因 3 —— 字符串没闭合
一个字符串值没用收尾的双引号结束,常见是因为字符串内部带了一个未转义的双引号:
JSON.parse('{"title":"He said "hello" to her"}')
// ^ 未转义的引号提前结束了字符串
// SyntaxError: Unexpected end of JSON input
// 正确 —— 内部引号用反斜杠转义:
JSON.parse('{"title":"He said \"hello\" to her"}')修复: 把内部双引号转义成 \",或者一开始就用 JSON.stringify() 来构造 JSON,而不是手工拼字符串。
原因 4 —— 空字符串
把空字符串或者只有空白的字符串传给 JSON.parse() 也会抛同样的错 —— 根本就没有输入可解析。
JSON.parse('') // SyntaxError: Unexpected end of JSON input
JSON.parse(' ') // SyntaxError: Unexpected end of JSON input
JSON.parse() // SyntaxError: Unexpected token u…(undefined 被转成字符串)这经常发生在表单字段、localStorage 的 key、或者环境变量是空的时候。
// 解析前先 guard
const raw = localStorage.getItem('session');
if (!raw) return null;
return JSON.parse(raw);原因 5 —— 流式响应读得太早
在消费流式 API 或 WebSocket 时,很容易在整条消息还没到齐之前就先去解析一个 chunk。
// ❌ 试图解析不完整的 chunk
ws.onmessage = (event) => {
const data = JSON.parse(event.data); // 可能不完整
};
// ✓ 缓冲到消息边界(应用层分帧)
let buffer = '';
ws.onmessage = (event) => {
buffer += event.data;
if (buffer.endsWith('\n')) { // 或者检查一个已知的分隔符
const data = JSON.parse(buffer.trim());
buffer = '';
}
};诊断截断:Content-Length 和流式读取器
两个具体技巧,用来确认响应是不是真的被截短了:
- 对比
Content-Length。 如果服务器设了,你可以直接检测到短读:const res = await fetch(url); const expected = Number(res.headers.get('Content-Length') ?? NaN); const text = await res.text(); if (Number.isFinite(expected) && text.length !== expected) { throw new Error(`truncated: got ${text.length} of ${expected} bytes`); }注意:
Content-Length在分块或压缩响应里可能不存在。 - 用流式 body 读取器读。 用
res.body.getReader()可以让你一块块观察数据到达,并察觉流在 payload 中间关闭 —— 这就是连接被断或代理超时的典型表现。如果流被中止,浏览器会以一个fetchrejection 形式暴露底层网络失败;配合解析器错误一起看就更准了。
快速诊断清单
- 在调用 parse 之前,打
typeof input和input.length的日志。 - 如果长度看起来太短,说明响应被截断了 —— 看网络日志。
- 如果长度是 0,说明来源(API、存储、环境变量)什么都没返回 —— 加一道 guard。
- 如果长度看起来对,就把字符串贴到 JSON Fix 去找结构问题。
在生产里优雅地兜底
function safeParse(text, fallback = null) {
if (!text || !text.trim()) return fallback;
try {
return JSON.parse(text);
} catch {
console.error('JSON parse failed, input length:', text.length);
return fallback;
}
}常见问题
「Unexpected end of JSON input」是由什么引起的?
解析器在还处于对象、数组或字符串内部时就走到了字符串末尾 —— 数据不完整。最常见的真实原因是 API 响应被截断;其他情况包括没闭合的括号、未结束的字符串、空字符串。
为什么没有行号或位置?
因为问题是内容缺失,而不是某个字符错了。解析器没有东西可以指 —— 它只是在结构闭合之前把输入用完了。
空字符串的情况怎么修?
解析前先加 guard:if (!raw || !raw.trim()) return fallback;。空表单字段、localStorage 中缺失的 key、未设置的环境变量是最常见的来源。
在生产里怎么优雅地处理?
用一个 safeParse 帮助函数把 JSON.parse() 包起来,失败时返回一个 fallback(见上面的代码片段),并把输入的长度打到日志里,便于区分截断和结构问题。完整的模式集请见 在 JavaScript 中处理坏掉的 JSON