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

Коротко: когда появляется ошибка 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 корректен и парсер доволен — проблема в форме данных:
- Двойное кодирование: весь документ прошёл
JSON.stringifyдважды, поэтому разбор даёт строку, а не объект. Еслиtypeof result === 'string', разберите ещё раз — но найдите и исправьте двойную сериализацию в источнике. - Повторяющиеся ключи молча перезаписываются:
{"name": "Tom", "name": "Jerry"}— допустимый JSON, большинство парсеров не жалуется, и побеждает второе значение. Это кусает при слиянии данных из нескольких источников; выявите проблему строгим валидатором JSON, пока он не укусил вас. - NDJSON приняли за один документ: потоковые API выдают по объекту на строку; разбор всего текста падает с
Unexpected non-whitespace character after JSON. Разделите по\nи разбирайте построчно. - Молчаливая потеря точности чисел: целые числа больше
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. Быстрый путь принятия решения
Сожмём всю статью в один порядок действий. При появлении ошибки сначала возьмите позицию и токен:
- Позиция 0 → проверьте три источника:
<(HTML), BOM, неверный тип; end of input→ проверьте пустое или обрезанное тело, затем ищите назад незакрытые скобки;Expected ...→ посмотрите на предыдущий символ; обычно это пропущенная запятая или кавычка;after JSON data→ несколько документов склеены; разделите по строкам;- Разбирается, но данные выглядят неверно → выполните четыре проверки «ложно здоровых» состояний.
Вместе с пошаговой диагностикой в статье как исправить ошибки разбора 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 и пользуйтесь этой таблицей как есть.
Позиция ошибки указывает на место, которое выглядит нормально, — что дальше?
Настоящая ошибка раньше. Типичные случаи: пропущенная запятая (сообщается на следующей строке) и пропущенная закрывающая скобка (сообщается в конце файла). Идите назад от указанной позиции токен за токеном или делите пополам: разберите первую половину отдельно, и граница первого неудачного фрагмента будет настоящим местом ошибки.