Uncaught SyntaxError: неожиданный токен в JSON при вызове JSON.parse() - как найти и исправить
Если в консоли браузера или в логах Node.js вы видите ошибку Uncaught SyntaxError: Unexpected token ... in JSON at position ..., значит метод JSON.parse() получил на вход строку, которая не является валидным JSON. Эта статья поможет быстро понять, где именно в строке находится «битый» символ, разобрать самые частые причины такой ошибки и научиться предотвращать её в будущем - как на фронтенде, так и на бэкенде.
Разберём конкретные кейсы: лишние запятые, одинарные кавычки, пустой ответ сервера, неэкранированные символы, BOM-метку и проблемы с fetch(). Каждый случай - с примером кода и готовым решением.
Что это такое
JSON.parse() - встроенный метод JavaScript, который превращает строку в формате JSON в обычный JS-объект или массив. Если строка не соответствует спецификации JSON (RFC 8259), парсер останавливается на первом «непонятном» символе и выбрасывает SyntaxError.
Типичный вид ошибки:
Uncaught SyntaxError: Unexpected token } in JSON at position 42
Uncaught SyntaxError: Unexpected end of JSON input
Uncaught SyntaxError: Unexpected token u in JSON at position 0Число после position - это индекс символа в строке (считая с нуля), на котором парсер «споткнулся». Именно с этого числа стоит начинать поиск проблемы.
Почему возникает эта ошибка
JSON - куда более строгий формат, чем JavaScript-объекты. Он не прощает того, что прощает сам JS-движок при создании литералов объектов. Основные причины:
Строка на самом деле не JSON - это HTML-страница с ошибкой 404/500, пустой ответ сервера или обычный текст.
Нарушен синтаксис JSON - лишняя запятая, одинарные кавычки вместо двойных, отсутствие кавычек у ключей.
Двойное парсение - объект, который уже является JS-объектом, по ошибке ещё раз передают в JSON.parse().
Невидимые символы - BOM (Byte Order Mark) в начале файла, непечатаемые управляющие символы внутри строки.
Обрезанный ответ - соединение прервалось, и пришла неполная JSON-строка.
Частые ошибки и проблемы
1. Парсинг уже распарсенного объекта
const data = { name: "Alex" };
JSON.parse(data); // Uncaught SyntaxError: Unexpected token o in JSONОбъект автоматически приводится к строке "[object Object]", а это не валидный JSON.
2. Лишняя запятая (trailing comma)
JSON.parse('{"name": "Alex", "age": 30,}'); // SyntaxError: Unexpected token }В отличие от объектных литералов JS, JSON не допускает запятую перед закрывающей скобкой.
3. Одинарные кавычки вместо двойных
JSON.parse("{'name': 'Alex'}"); // SyntaxError: Unexpected token 'JSON-спецификация требует только двойные кавычки - и для ключей, и для строковых значений.
4. Ключи без кавычек
JSON.parse('{name: "Alex"}'); // SyntaxError: Unexpected token nВ валидном JS-объекте так писать можно, в JSON - нет: все ключи обязаны быть в двойных кавычках.
5. Пустая строка или undefined от сервера
fetch('/api/user')
.then(res => res.json()) // здесь может упасть с той же ошибкойЕсли сервер вернул пустое тело ответа (например, при ошибке 204 или 500 без JSON-тела), res.json() внутри вызовет JSON.parse(''), что даст Unexpected end of JSON input.
6. HTML вместо JSON
Самый частый случай в проде: бэкенд вернул страницу ошибки (<!DOCTYPE html>...) вместо ожидаемого JSON - например, из-за 404, редиректа на страницу логина или 500-й ошибки сервера. Парсер падает на самом первом символе <.
Как исправить: пошаговое решение
Шаг 1. Найдите проблемный символ по позиции
Возьмите число из текста ошибки и выведите окружающий контекст:
function debugJSON(str, position) {
console.log(str.slice(Math.max(0, position - 20), position + 20));
}
try {
JSON.parse(rawString);
} catch (e) {
const match = e.message.match(/position (\d+)/);
if (match) debugJSON(rawString, Number(match[1]));
console.error(e.message);
}Это сразу покажет, какой именно фрагмент строки невалиден.
Шаг 2. Проверьте, что вы вообще получили строку
console.log(typeof rawString, rawString);Если это объект - JSON.parse() здесь не нужен вовсе, используйте данные напрямую.
Шаг 3. Оберните парсинг в try/catch
Никогда не вызывайте JSON.parse() «вслепую» в продакшен-коде:
function safeParse(str, fallback = null) {
try {
return JSON.parse(str);
} catch (e) {
console.error('Не удалось распарсить JSON:', e.message);
return fallback;
}
}Шаг 4. Проверяйте ответ сервера перед парсингом
Для fetch() важно проверить статус и content-type до вызова .json():
fetch('/api/user')
.then(async res => {
const text = await res.text();
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${text.slice(0, 100)}`);
}
try {
return JSON.parse(text);
} catch {
throw new Error('Сервер вернул не-JSON ответ: ' + text.slice(0, 100));
}
})
.then(data => console.log(data))
.catch(err => console.error(err.message));Такой подход сразу покажет, пришёл ли HTML вместо JSON или ответ оказался пустым.
Шаг 5. Уберите BOM-символ, если он есть
function stripBOM(str) {
return str.charCodeAt(0) === 0xFEFF ? str.slice(1) : str;
}
JSON.parse(stripBOM(rawString));BOM часто появляется в файлах, сохранённых через Windows-редакторы, и невидим в обычном просмотре кода.
Шаг 6. Валидируйте JSON перед отправкой/чтением файла
Для конфигов и .json-файлов полезно прогонять их через линтер или онлайн-валидатор (например, jsonlint.com) перед коммитом - это исключает ошибки из-за лишних запятых и кавычек.
Часто задаваемые вопросы (FAQ)
Почему ошибка пишет «Unexpected end of JSON input», а не конкретный символ? Это значит, что строка обрывается раньше, чем ожидал парсер - например, не хватает закрывающей скобки } или ], либо строка вообще пустая.
Можно ли использовать JSON.parse() для парсинга JS-объекта с комментариями? Нет, стандартный JSON не поддерживает комментарии. Если вам нужны комментарии в конфиге, используйте формат JSON5 или YAML с соответствующей библиотекой.
Чем JSON.parse() отличается от eval() для разбора JSON-строк? JSON.parse() безопасен и не выполняет произвольный код, тогда как eval() исполняет любую переданную строку как JavaScript - это серьёзная угроза безопасности, поэтому eval() для парсинга JSON использовать нельзя.
Как распарсить JSON с одинарными кавычками без переписывания вручную? Можно аккуратно заменить кавычки через регулярное выражение, но это хрупкое решение. Надёжнее - потребовать от источника данных валидный JSON или использовать парсер, толерантный к JS-литералам, например JSON5.parse().
Почему ошибка появляется только в продакшене, а локально всё работает? Чаще всего причина - разное поведение сервера: на проде включены другие обработчики ошибок, прокси или CDN могут возвращать HTML-страницу ошибки вместо JSON-ответа API.
Как добавить кастомную обработку ошибок второго аргумента JSON.parse()? Второй аргумент JSON.parse() - это функция reviver, она нужна для трансформации значений после успешного парсинга, а не для перехвата синтаксических ошибок. Для обработки самих ошибок парсинга нужен try/catch.
Можно ли распарсить JSON с trailing comma, не убирая её вручную? Стандартный JSON.parse() - нет. Для таких случаев есть более мягкие парсеры (например, JSON5 или comment-json), которые принимают расширенный синтаксис.
Как проверить валидность JSON без try/catch, заранее? Полноценной встроенной функции-валидатора нет - try/catch остаётся стандартным и самым надёжным способом. Для статических файлов удобно использовать внешние валидаторы или JSON Schema.
Полезные советы и лучшие практики
Всегда оборачивайте JSON.parse() в try/catch в коде, который работает с внешними данными (API, localStorage, файлы).
При работе с fetch() сначала проверяйте response.ok и при необходимости content-type, прежде чем вызывать .json().
Логируйте не только текст ошибки, но и фрагмент исходной строки рядом с позицией сбоя - это экономит время на отладке.
Не парсите то, что уже является объектом: проверяйте typeof перед вызовом.
Для конфигурационных файлов используйте линтеры (ESLint с плагином для JSON, или отдельные JSON-валидаторы) в CI/CD, чтобы ловить trailing comma и опечатки до деплоя.
Если структура данных предсказуема, рассмотрите валидацию через JSON Schema (например, с библиотекой ajv) - это поможет ловить не только синтаксические, но и смысловые ошибки.
Итог
Uncaught SyntaxError: Unexpected token ... in JSON почти всегда означает одно из трёх: на вход парсеру попал не-JSON текст (HTML, пустая строка, уже готовый объект), в строке нарушен синтаксис JSON (лишняя запятая, одинарные кавычки, ключи без кавычек), либо ответ пришёл обрезанным. Используйте позицию из текста ошибки, чтобы быстро найти проблемный фрагмент, всегда оборачивайте JSON.parse() в try/catch и проверяйте ответы сервера перед парсингом - это избавит от большинства подобных сбоев в продакшене.
Комментарии
Чтобы оставить комментарий, войдите в аккаунт.