Гайды

Почему JSON не парсится (Unexpected token) — 3 причины, и в JSON нет ни одной

Иллюстрация: аккуратная посылка внутри мятой обёртки, которую никто не снял

Ты видишь в консоли что-то такое:

SyntaxError: Unexpected token ` in JSON at position 0

И идёшь искать ошибку в JSON. Не там ищешь.

Позиция 0 — это первый символ. Парсер споткнулся, не начав работу: он ещё не дошёл ни до кавычек, ни до запятых. Это значит одно — ему дали не JSON. Целиком не JSON, с самого начала.

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

console.log(JSON.stringify(raw.slice(0, 100)))

Именно JSON.stringify, а не просто console.log: он покажет невидимое — переносы, кавычки, пробелы. Первые сто символов почти всегда назовут виновника. А в сообщении об ошибке первым делом читай сам символ: он и есть подсказка. Разберём три случая, которые покрывают почти всё.

Причина 1. Модель обернула JSON в markdown

Самая частая, если в проекте есть ИИ. Ты просил модель вернуть JSON — она вернула. Заботливо, в блоке подсветки:

```json
{"name": "Аня", "age": 30}
```

Внутри — идеальный JSON. Снаружи — три обратные кавычки и слово json. Для JSON.parse это мусор с первого же символа. Отсюда и «Unexpected token `».

Модель не глючит. Её учили на markdown, где код принято оборачивать. Она делает то, что делают люди.

Как проверить: увидел в ошибке обратную кавычку — диагноз готов.

Как чинить. Костыль на сейчас — снять обёртку:

const clean = raw.trim().replace(/^```(?:json)?\s*/i, '').replace(/```$/i, '')
const data = JSON.parse(clean)

Работает, но это лечение симптома. Модель может обернуть иначе, добавить «Вот ваш JSON:» перед блоком, приписать пояснение после. Ты будешь чинить регулярку до конца жизни.

Настоящее решение — не просить JSON словами, а потребовать его на уровне API: структурированный вывод заставляет модель отдавать ответ строго по схеме. Тогда обёртки не будет в принципе — и парсить станет нечего.

Причина 2. Сервер прислал HTML вместо данных

Ошибка выглядит так:

SyntaxError: Unexpected token < in JSON at position 0

Угловая скобка в начале — это <!DOCTYPE html>. Тебе пришла веб-страница.

Что произошло: запрос ушёл не туда или упал, а сервер вежливо вернул человеческую страницу ошибки — 404 или 500. Твой код увидел ответ, обрадовался и попытался распарсить страницу как данные.

Как проверить: глянь в браузере вкладку «Сеть» — статус запроса и вкладку Response. Увидишь ту самую страницу.

Как чинить — не глотать ошибки молча:

const res = await fetch(url)
if (!res.ok) throw new Error(`${res.status} на ${url}`)

const ct = res.headers.get('content-type') ?? ''
if (!ct.includes('application/json')) {
  throw new Error(`Ждали JSON, пришло ${ct}: ${(await res.text()).slice(0, 200)}`)
}

const data = await res.json()

Пять строк — и вместо загадочного Unexpected token < ты получаешь «404 на /api/usres». С опечаткой, которую видно сразу.

Рядом живёт близкий родственник — Unexpected token u. Буква u — это начало слова undefined: ты парсишь переменную, в которой ничего нет. Обычно потому, что забыл await. Это отдельная классика, разобранная тут: почему fetch возвращает undefined.

Причина 3. Это JSON, но по чужим правилам

Дошло до сюда — значит, тебе действительно прислали объект, и ошибка указывает не на позицию 0, а куда-то в середину. Теперь можно смотреть на сам текст.

JSON строже, чем кажется. Он не прощает того, что спокойно съедает JavaScript и любит вставлять текстовый редактор:

  • Одинарные кавычки. {'name': 'Аня'} — не JSON. Только двойные.
  • Висячая запятая. {"a": 1,} — в JS законно, в JSON ошибка.
  • Умные кавычки. {"name": "Аня"} с типографскими «ёлочками» вместо " — беда всех, кто копирует из документа или мессенджера. Глазами не отличить, парсер падает.
  • Комментарии. Их в JSON нет вообще.

Как проверить: ткни текст в любой валидатор JSON — он покажет строку и символ.

Как чинить: если JSON генерируешь ты — не собирай его строками, никогда. Собери объект и отдай JSON.stringify — он не забудет кавычку. Если JSON пришёл извне — виноват отправитель, чини у источника, а не регулярками у себя.

Порядок действий

Держи короткую шпаргалку:

  1. Распечатай первые 100 символов через JSON.stringify.
  2. Посмотри на символ из ошибки: ` → обёртка модели, < → HTML, u → undefined.
  3. Позиция 0 — проблема снаружи JSON. Не позиция 0 — теперь можно и в валидатор.
  4. Почини причину, а не текст. Обёртка — включи структурированный вывод. HTML — проверяй статус ответа. Кривые кавычки — генерируй через stringify.

Главное, что стоит унести: JSON.parse почти никогда не врёт. Он честно говорит «это не JSON» — и обычно оказывается прав.

Почему модель добавляет ```json, если я прямо попросил «только JSON»?

Потому что просьба словами — это пожелание, а не гарантия. Модель предсказывает правдоподобный текст, а в текстах, на которых её учили, JSON почти всегда лежит в блоке кода. Иногда она послушается, иногда нет — почему модель игнорит инструкции. Гарантию даёт только схема на уровне API.

Стоит ли просто обернуть JSON.parse в try/catch?

Обязательно — но это защита, а не диагноз. try/catch спасает приложение от падения, однако в блоке catch обязательно логируй сам текст, который не распарсился. Иначе ты знаешь, что «что-то сломалось», и не знаешь ничего больше.

Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение
Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →