JSON Toolkit

Справочник сообщений об ошибках JSON: от текста ошибки к исправлению

Главное: Найдите причину по точному сообщению JSON.parse: 12 частых ошибок в Chrome, Firefox и Safari, включая HTML-страницы ошибок, обрыв данных и двойной разбор.

Продвинутый~8 мин чтенияОпубликовано 2026-10-30Обновлено 2026-11-04
Обложка справочной таблицы сообщений об ошибках JSON

Коротко: когда появляется ошибка JSON.parse, сначала возьмите позицию и токен; позиция 0 означает проверку HTML-ответа, BOM или неверного типа, а сообщение «Expected» означает взгляд на предыдущий символ — не перечитывайте построчно от указанной позиции.

Текст ошибки — не шум, а ответ. Парсер сообщает, на каком символе он споткнулся, что ожидал и что получил на самом деле. Поиск по тексту ошибки экономит больше времени, чем ручное переписывание всего документа.

В каждой ошибке JSON.parse() есть три части: позиция (смещение символа или строка/столбец), ожидание (Expected...) и реальность (Unexpected token X или «xxx is not valid JSON»). Движки формулируют их по-разному — Chrome и Node (V8) говорят Expected ',' or '}' after property value in JSON at position 25, Firefox — JSON.parse: expected ',' or '}' after property value in object at line 3 column 1, Safari — JSON Parse error: Expected ',' or '}', — но все три указывают на один и тот же синтаксический разрыв. Когда ищете ошибку в справочнике, игнорируйте формулировку и забирайте три факта: какой символ, что ожидалось, что пришло.

1. Таблица из 12 ошибок

В левом столбце — самый узнаваемый фрагмент из консоли (формулировка V8). В скобках — варианты Firefox/Safari.

Текст ошибки (V8) Настоящая причина Исправление
Unexpected token < in JSON at position 0 API вернул HTML (страницу 404, шаблон 500, редирект на вход, заглушку прокси), а не JSON Сначала проверьте response.status и Content-Type; JSON не трогайте
Unexpected end of JSON input Вход пуст или обрезан: пустое тело, 204, прерванная загрузка, копия без последней строки Обработайте пустой случай, сравните Content-Length, чтобы найти обрыв
Unexpected token o / "[object Object]" is not valid JSON Уже разобранный объект снова передали в JSON.parse() и он был приведён к строке Проверьте typeof; если это объект, используйте его напрямую
"undefined" is not valid JSON Переменной так и не присвоили значение (JSON.parse(undefined)) Инициализируйте значения по умолчанию; проверяйте перед разбором
Expected double-quoted property name in JSON Завершающая запятая ({"a": 1,}); парсер ждёт очередной ключ и находит } Удалите запятую после последнего элемента
Expected property name or '}' in JSON Ключ без кавычек или в одинарных кавычках ({name: "Ada"}, {'name': 'Ada'}) Заключите каждый ключ в двойные кавычки
Expected ',' or '}' after property value in JSON Пропущенная запятая ({"a": 1 "b": 2}) или комментарий после значения Добавьте запятую; удалите комментарий
Unexpected token '/' Комментарий // или /* */ внутри JSON Удалите его или разбирайте конфигурацию как JSONC
Bad control character in string literal Необработанный перевод строки, табуляция или другой управляющий символ U+0000–U+001F внутри строки Запишите \n; генерируйте через JSON.stringify
Unterminated string in JSON Строка открыта " и не закрыта, либо внутренняя кавычка не экранирована Экранируйте внутренние кавычки как \"
Unexpected non-whitespace character after JSON Два документа в одном тексте: {}{}, NDJSON, обёртка JSONP Разделите по строкам и разбирайте каждую или используйте парсер NDJSON
JSON.parse is not a function Ваша собственная переменная затеняет глобальный JSON Переименуйте переменную; никогда не называйте её JSON

Первые две заслуживают более внимательного взгляда, потому что ни одна из них не означает «ваш JSON неправильный» — они означают «то, что вы получили, не то, что вы думаете».

2. Три частых источника ошибки с позицией 0

Позиция 0 означает, что самый первый символ недопустим. Если ничего подозрительного не видно, скорее всего, символ невидим. Три обычных подозреваемых:

Первое: <, то есть HTML. Это самая частая ошибка разбора в продакшене. Неверный URL, истёкшая сессия с редиректом от шлюза или шаблон ошибки сервера — тело начинается с <!DOCTYPE html>, и парсер сдаётся на первом символе. Правильный шаг — открыть панель Network и прочитать сырой ответ, а затем исправить вызов API, а не JSON.

Второе: BOM или символы нулевой ширины. Файлы, сохранённые «Блокнотом» Windows, часто начинаются с UTF-8 BOM (\uFEFF), а текст, скопированный из веб-страниц или мессенджеров, может нести пробелы нулевой ширины (\u200B–\u200D). Для вас они невидимы, для парсера — очевидны:

// Strip BOM and zero-width characters before parsing
const cleaned = raw.replace(/^\uFEFF/, '').replace(/[\u200B-\u200D]/g, '');
const data = JSON.parse(cleaned);

Третье: неверный тип. JSON.parse(undefined) сообщает "undefined" is not valid JSON; JSON.parse(someObject) сообщает Unexpected token o (объект был приведён к "[object Object]", а o недопустим в позиции 1). Оба означают «данные уже разобраны» или «данных никогда не было». Проверяйте тип, а не синтаксис:

let data = text;
if (typeof data === 'string') {
  data = JSON.parse(data); // Parse only when it really is a string
}

3. Позиция указывает вниз по тексту, а причина — выше

Две ошибки вводят в заблуждение относительно места, и это две самые неверно читаемые строки таблицы:

  • Пропущенная запятая: {"a": 1 "b": 2} сообщает Expected ',' or '}' after property value в строке с "b". Сообщение верное, но исправлять нужно конец предыдущей строки.
  • Пропущенная закрывающая скобка: при глубокой вложенности без одного } или ] парсер проглатывает всё до конца ввода и сообщает Unexpected end of JSON input. Позиция — конец файла, а настоящий пропуск может быть гораздо раньше. Ищите назад ближайшую незакрытую { или [ — подсчёт пар надёжнее всматривания.

Общая техника — деление пополам: отрежьте текст перед указанной позицией и разберите его отдельно. Если разбирается, проблема рядом с указанной позицией; если нет — делите дальше назад. Граница первого неудачного фрагмента и есть настоящее место ошибки. Для больших файлов сначала отформатируйте документ — при выровненных отступах пропущенный уровень виден сразу (онлайн-форматтер JSON для этого подходит).

4. Четыре «ложно здоровых» состояния: разбор проходит, но поведение неверное

Хуже ошибки — её отсутствие при неверных данных. Во всех четырёх случаях JSON корректен и парсер доволен — проблема в форме данных:

  1. Двойное кодирование: весь документ прошёл JSON.stringify дважды, поэтому разбор даёт строку, а не объект. Если typeof result === 'string', разберите ещё раз — но найдите и исправьте двойную сериализацию в источнике.
  2. Повторяющиеся ключи молча перезаписываются: {"name": "Tom", "name": "Jerry"} — допустимый JSON, большинство парсеров не жалуется, и побеждает второе значение. Это кусает при слиянии данных из нескольких источников; выявите проблему строгим валидатором JSON, пока он не укусил вас.
  3. NDJSON приняли за один документ: потоковые API выдают по объекту на строку; разбор всего текста падает с Unexpected non-whitespace character after JSON. Разделите по \n и разбирайте построчно.
  4. Молчаливая потеря точности чисел: целые числа больше 2^53 - 1 (идентификаторы snowflake) возвращаются с обнулёнными последними цифрами, и никакой ошибки нет. Передавайте идентификаторы строками.

Общая нить: парсер считает, что всё в порядке. Поэтому когда данные API выглядят неверно, сначала выведите первые 80 символов сырого текста и его typeof — подтвердите форму, прежде чем обсуждать содержимое.

5. Сделайте ошибки невозможными на этапе записи

Справочная таблица — инструмент после факта; профилактика — инструмент до факта. Четыре привычки перехватывают большинство ошибок до их появления:

// 1. Two checks before parsing any response
const response = await fetch('/api/users');
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: the API returned no data`);
}
const contentType = response.headers.get('content-type') ?? '';
if (!contentType.includes('application/json')) {
  throw new Error(`Expected JSON, got ${contentType}`);
}
const data = await response.json(); // 2. json() already parses — do not parse again
  • Генерируйте сериализацией, а не склейкой: JSON.stringify() правильно обрабатывает кавычки, переводы строк и управляющие символы; строки, собранные руками, — главный источник Bad control character и Unterminated string.
  • Всегда перехватывайте: выброшенная ошибка JSON.parse — это синхронный сбой, если её никто не ловит. Для недоверенного ввода — localStorage, внешних файлов — в блоке catch нужно значение по умолчанию или явное сообщение об ошибке.
  • Структуру делегируйте Schema: допустимый синтаксис не означает допустимую структуру. Отсутствующие поля и неверные типы относятся к валидации JSON Schema на границе; см. что решает JSON Schema.

6. Быстрый путь принятия решения

Сожмём всю статью в один порядок действий. При появлении ошибки сначала возьмите позицию и токен:

  1. Позиция 0 → проверьте три источника: < (HTML), BOM, неверный тип;
  2. end of input → проверьте пустое или обрезанное тело, затем ищите назад незакрытые скобки;
  3. Expected ... → посмотрите на предыдущий символ; обычно это пропущенная запятая или кавычка;
  4. after JSON data → несколько документов склеены; разделите по строкам;
  5. Разбирается, но данные выглядят неверно → выполните четыре проверки «ложно здоровых» состояний.

Вместе с пошаговой диагностикой в статье как исправить ошибки разбора JSON 90% проблем разбора решаются за пять минут. Держите сам текст ошибки под рукой — это журнал отладки, который парсер написал для вас; его формулировки меняются раз в три года, и один поиск окупает год работы.

Что означает «Unexpected token in JSON»?

Парсер наткнулся на символ, недопустимый в этом месте грамматики JSON. Токен — это отвергнутый символ: < обычно означает, что API вернул HTML, ' — одинарные кавычки, / — комментарий, а обычная буква — ключ без кавычек или литерал, существующий только в JavaScript, вроде undefined или NaN. Сначала прочитайте токен, затем позицию, и причина сузится до одной.

Как исправить «Unexpected end of JSON input»?

Два источника: пустой ввод (пустое тело ответа или пустая строка в localStorage) или обрезанный ввод (прерванная загрузка, копия без последней строки, ограничение размера). Обработайте пустой случай через if (!text) return; затем проверьте полноту и закройте ближайшую незакрытую {, [ или ". Учтите, что ошибка может приходить и из глубоко пропущенной скобки — позиция просто совпала с концом файла.

Почему Chrome, Firefox и Safari сообщают об ошибках по-разному?

Три браузера используют разные JS-движки (V8, SpiderMonkey, JavaScriptCore), и каждый формулирует ошибки по-своему; MDN документирует все официальные варианты. Но все три ошибки несут одни и те же три элемента — позицию, ожидание и реальность — с взаимно однозначной семантикой. Переведите их в формулировку V8 и пользуйтесь этой таблицей как есть.

Позиция ошибки указывает на место, которое выглядит нормально, — что дальше?

Настоящая ошибка раньше. Типичные случаи: пропущенная запятая (сообщается на следующей строке) и пропущенная закрывающая скобка (сообщается в конце файла). Идите назад от указанной позиции токен за токеном или делите пополам: разберите первую половину отдельно, и граница первого неудачного фрагмента будет настоящим местом ошибки.