Uncaught SyntaxError: неожиданный токен в JSON при вызове JSON.parse() - как найти и исправить

mr. Cooper 3 часа назад Веб-разработка
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 и проверяйте ответы сервера перед парсингом - это избавит от большинства подобных сбоев в продакшене.

Комментарии

Пока нет комментариев. Будьте первым, кто напишет.

Чтобы оставить комментарий, войдите в аккаунт.

Похожие статьи