Как исправить ошибки разбора JSON: пошаговая проверка Unexpected token
Главное: Превратите ошибку JSON.parse в номер строки и символа, а затем исключите шесть причин: завершающие запятые, одинарные кавычки, комментарии, управляющие символы, BOM и обрыв данных.

Коротко: при ошибке разбора читайте указанные строку и столбец, чтобы найти первый недопустимый символ, и затем решите, дело в пропущенной кавычке, пропущенной запятой, завершающей запятой, BOM или обрыве данных, — не перечитывайте весь документ с начала.
Сообщение об ошибке никогда не говорит «что именно не так»; оно говорит лишь «где парсер больше не смог продолжить». Эти две позиции часто оказываются разными местами.
1. Сначала превратите ошибку в координаты
Один и тот же битый JSON даёт совершенно разные ошибки в разных языках, но все их можно свести к «какая строка, какой символ»:
| Среда | Типичная ошибка | Как её читать |
|---|---|---|
| Браузер / Node | Unexpected token } in JSON at position 42 |
position — это смещение символа с нуля, а не номер строки |
| Node 20+ | Unexpected non-whitespace character after JSON at position 42 |
значит, первая часть корректна, а лишнее содержимое начинается после 42 |
| Java Jackson | Unexpected character ('}' (code 125)): was expecting ... at [Source: (String)..."..." ; line: 3, column: 1] |
сразу даёт строку и столбец — самый удобный вариант |
| Python | json.decoder.JSONDecodeError: Expecting ',' delimiter: line 3 column 1 (char 42) |
char — это смещение, а важны строка и столбец |
| Go | invalid character '}' looking for beginning of object key string |
даёт только символ, поэтому позицию приходится находить самому |
Когда позиция известна, вставьте JSON в онлайн-парсер JSON: инструмент ставит курсор в место ошибки и подсвечивает номер строки и столбца — этот шаг экономит 90% всматривания в символы.
2. Шесть частых ошибок
1. Завершающая запятая
{
"name": "json",
"version": 1,
}
Перед } стоит лишняя запятая. Спецификация JSON не допускает завершающих запятых, а литералы объектов JavaScript допускают, поэтому конфигурация, скопированная из кода, чаще всего несёт её с собой. Исправление: удалите последнюю запятую; если эти данные изначально живут в JS-файле, перейдите на конвертацию в JSON5, а не загоняйте их в JSON силой.
2. Одинарные кавычки и ключи без кавычек
{ 'name': 'json' }
{ name: "json" }
Строки JSON могут использовать только двойные кавычки, и ключи тоже должны быть строками в двойных кавычках. Эти две формы — послабление синтаксиса JSON5 / JavaScript, а не JSON. Двойную кавычку внутри строки нужно записывать как \", а обратную косую черту — как \\.
3. Комментарии
{
// Timeout in milliseconds
"timeout": 3000
}
В грамматике RFC 8259 для комментариев места нет. Либо удалите комментарии, либо сохраните файл как .jsonc / .json5 и используйте парсер, поддерживающий комментарии. О компромиссах разных «схем комментирования» читайте в статье Как писать комментарии в JSON.
4. Необработанные переводы строк и управляющие символы внутри строк
{"sql": "SELECT *
FROM t"}
Строки JSON не могут содержать необработанные переводы строки, табуляцию и управляющие символы U+0000–U+001F; их нужно записывать как \n, \t, \u0009. Когда вставляете многострочный текст в JSON, сначала экранируйте его в редакторе или обработайте единообразно с помощью минификации и экранирования JSON.
5. Заголовок BOM
UTF-8, сохранённый «Блокнотом» Windows, несёт три байта EF BB BF. JSON.parse при встрече с BOM сообщает Unexpected token, а глазами его вообще не видно. Как проверить: посмотрите начало файла в шестнадцатеричном виде или вставьте содержимое в онлайн-инструмент и проверьте, отображается ли первый символ как . Исправление: сохраните файл в варианте «UTF-8 без BOM».
6. Обрезанное содержимое
Лог, в который попали только первые 4096 символов, ответ API, обрезанный шлюзом, прерванная загрузка — отличительная черта этого класса в том, что «ошибка находится в конце», а количество скобок не сходится. Сначала посчитайте скобки: если закрывающие } и ] не совпадают с открывающими, это обрыв, а не синтаксическая ошибка. В таком случае ничего не исправляйте — идите за полными данными.
3. Две ложные тревоги, связанные с китайским текстом и экранированием
Китайский превращается в \u4e2d\u6587: это допустимое экранирование JSON, а не «кракозябры». После разбора значение восстанавливается в китайский текст; форма \u видна, только если смотреть на сырую строку напрямую. Когда нужно показать это людям, просто выведите результат через инструмент форматирования; об стратегии экранирования стоит думать, только если китайский текст должен выжить в URL или логе.
Китайский превращается в кракозябры вроде 涓枃: это проблема кодировки, а не JSON. Так бывает, когда байты UTF-8 декодируются как GBK и получается характерный узор «три символа вместо одного иероглифа». Проверьте заголовок ответа Content-Type: application/json; charset=utf-8 и уточните, задаётся ли encoding='utf-8' явно при чтении файла.
4. Пятиминутный порядок починки
- Вставьте в инструмент: сначала с помощью онлайн-парсера найдите первую ошибку и исправляйте только одну за раз;
- Проверяйте после каждого исправления: заново разбирайте после каждого изменения, чтобы исправление нескольких мест сразу не позволило новым ошибкам замаскировать старую;
- Минифицируйте один раз: уберите пробелы через минификацию и разберите снова; это отличает «структурные ошибки» от «проблем с пробелами и переводами строк»;
- Сравните структуру: если после изменения внезапно появилась ошибка, сравните с предыдущей версией через JSON diff; разница обычно и есть та самая одна запятая;
- Валидация на уровне полей: если синтаксис проходит, а бизнес-логика данные не принимает (нет поля, неверный тип), это уже валидация JSON Schema, а не синтаксическая проблема.
5. Почему один из двух «одинаковых» фрагментов JSON разбирается, а другой нет
Самая частая причина — невидимые символы: полноширинная запятая ,, полноширинные кавычки “”, неразрывный пробел U+00A0, пробел нулевой ширины U+200B. На экране они почти неотличимы от полуширинных аналогов, но ни один из них не входит в грамматику JSON.
Есть только один способ проверить: скопируйте фрагмент вокруг подозрительного символа и просмотрите кодовые точки посимвольно. Следующий код в консоли браузера выведет все символы, которые не являются ASCII и не попадают в китайские диапазоны:
[...text].filter((ch) => {
const code = ch.codePointAt(0);
return code > 127 && !(code >= 0x4e00 && code <= 0x9fff);
});
Если в выводе появились 0x201c (левая двойная кавычка), 0xff0c (полноширинная запятая) или 0x00a0, виновник найден.
6. Профилактика дешевле ремонта
- Прогоняйте проверку разбора конфигурационных файлов перед коммитом; в CI достаточно одной строки:
node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))"; - Генерируйте ответы API библиотекой сериализации; никогда не склеивайте строки вручную — только собранные руками строки дают завершающие запятые и пропущенные кавычки;
- При логировании JSON сначала вызывайте
JSON.stringify, а не подставляйте его в шаблон, чтобы недописанный объект не приняли за полное сообщение.
Вопросы и ответы
Как быстро найти место синтаксической ошибки JSON?
Возьмите position или строку и столбец, которые сообщает парсер; position — это смещение символа с нуля, а не номер строки, тогда как Java Jackson и Python сразу сообщают строку и столбец. Вставьте JSON в онлайн-парсер JSON, и инструмент поставит курсор в место ошибки и подсветит строку со столбцом. Если ошибка находится в начале, проверьте начало файла на BOM и поищите невидимые символы.
Почему конфигурация, скопированная из кода, так часто несёт завершающую запятую?
Спецификация JSON запрещает завершающие запятые, а литералы объектов JavaScript их допускают, поэтому конфигурация, вытащенная из JS-кода, легко приносит её с собой. Исправление простое — удалить последнюю запятую; если данные всё равно относятся к JS-файлу, конвертация в JSON5 подходит лучше, чем насильно загонять их в JSON.
Можно ли разобрать конфигурационный файл с комментариями как обычный JSON?
Нет — в грамматике RFC 8259 для комментариев места нет, поэтому прямой разбор выдаст ошибку. Либо удалите комментарии, либо сохраните файл как .jsonc / .json5 и используйте парсер с поддержкой комментариев. О компромиссах схем комментирования читайте в статье Как писать комментарии в JSON.