Не вдалося розпарсити відповідь: дані є, сенсу немає

Коротко

  • Повідомлення не вдалося розпарсити відповідь означає: програма вже отримала дані, але не змогла перетворити їх на очікувану структуру — найчастіше на JSON або XML.
  • Це рідко «падіння інтернету». Частіше клієнт чекає об’єкт на кшталт {“ok”: true}, а отримує HTML-сторінку помилки, порожнє тіло, обрізаний рядок або текст із зайвою комою.
  • Класичний слід у консолі браузера — Unexpected token < in JSON at position 0: перший символ «<» видає HTML замість JSON.
  • Користувачу варто почати з інкогніто, іншої мережі й очищення даних саме цього сайту, а не з перевстановлення Windows.
  • Розробнику — спочатку подивитися HTTP-статус, заголовок Content-Type і перші 200 символів тіла, і лише потім лізти в JSON.parse.
  • Повторювати оплату чи відправку форми «на всяк випадок» небезпечно: запит міг пройти, зламалася лише обробка відповіді.

Якщо коротко звести все до однієї дії: не гадайте, а подивіться сиру відповідь. У дев’яти випадках із десяти причина лежить у першому рядку тіла, а не в «загадковому збої протоколу».

Повідомлення не вдалося розпарсити відповідь з’являється тоді, коли клієнт уже щось отримав і намагається це прочитати за правилами формату. Парсер очікує чітку граматику: дужки, двокрапки, подвійні лапки, правильне кодування. Натрапив на зайвий символ — зупинився. Мережа при цьому може бути ідеальною, сервер — живим, кнопка «Оплатити» — зеленою. Просто відповідь прийшла «не тією мовою».

У практиці я найчастіше бачу не зламаний JSON, а HTML. Шлюз віддав сторінку 502, корпоративний проксі — форму логіну, Cloudflare — challenge, PHP перед JSON дописав Warning. Фронтенд чесно викликає response.json() і падає. Людина читає українською «не вдалося розпарсити» і думає, що в неї вірус. Ні. Просто клієнт і сервер не домовилися про формат.

Нижче — як це розібрати без шаманства: що ламається всередині, де ловити слід, що робити з телефону, що перевіряти в коді, і чому зайвий клік по «Повторити» іноді гірший за саму помилку.

Що насправді означає це повідомлення

Парсинг — це розбір тексту за правилами. Не «магія API», не шифрування, не антивірус. Програма бере рядок і намагається скласти з нього об’єкт: поля, масиви, числа, true/false/null. Правила жорсткі. Один зайвий символ — і розбір зупиняється. Звідси й формулювання: відповідь є, структури немає.

У вебі це майже завжди JSON. У касовому ПЗ і обміні з банківським терміналом — теж JSON, рідше XML. У iOS система може показати NSURLErrorCannotParseResponse: відповідь на запит не вдалося розібрати. Браузерна консоль любить інше формулювання — SyntaxError від JSON.parse. Різні етикетки, одна суть: очікуваний формат і фактичне тіло розійшлися.

Важлива різниця, яку новачки постійно змішують. Помилка парсингу — це синтаксис. Помилка валідації — це вже зміст. Якщо прийшло {“amount”: “сто”} замість числа, JSON.parse може пройти, а бізнес-логіка — ні. Повідомлення «не вдалося розпарсити» якраз про перше: парсер навіть об’єкт не зібрав. MDN Web Docs прямо каже: JSON.parse кидає SyntaxError, щойно трапляється неправильний синтаксис. Не «майже JSON». Або валідно, або виняток.

Ще один нюанс. HTTP-статус 200 не гарантує, що всередині JSON. Статус 500 не гарантує, що всередині HTML. Дивитися треба тіло. Я бачив API, яке на 200 віддавало рядок «OK», і axios чесно падав, бо чекав об’єкт. І навпаки: 404 з акуратним {“error”:”not_found”} парситься без проблем. Статус і формат — різні шари.

Де ця помилка виринає в реальному житті

Формулювання українською люблять локалізовані продукти: веб-кабінети, мобільні додатки банків і доставки, облікові системи, чат-боти, касове ПЗ. Англійською те саме звучить як failed to parse response, cannot parse response, parsingFailed. Якщо бачите будь-яке з них — шукайте розрив між очікуванням і тілом відповіді, а не «поганий Wi-Fi» як першу гіпотезу.

Де бачите Типовий слід Що підозрювати першим
Сайт або кабінет у браузері Unexpected token <, SyntaxError, failed to parse HTML-сторінка помилки, логін, 404 замість API
iPhone / iPad NSURLErrorCannotParseResponse Проміжний проксі, дивна відповідь шлюзу, обрізане тіло
Android-додаток JSONException, MalformedJsonException Те саме: HTML, gzip «каша», інше кодування
Каса, POS, банківський термінал Не вдалося розпарсити відповідь JSON Лапки й спецсимволи в назві точки, обрізаний пакет
Чат-бот, інтеграція з ШІ failed to parse model response Markdown-огорожа “`json:disable-run

Таблиця навмисно груба. Реальність буває дрібніша: один «м’який» перенос рядка всередині рядка, один BOM на початку файлу, одна «ялинка» в назві магазину. Але напрямок пошуку вона задає правильно — від першого символу тіла, а не від перевстановлення додатка.

Окремо про каси. У реліз-нотах Торгсофт для версії 2022.0.36 прямо описано виправлення: помилка «Не вдалося розпарсити відповідь JSON» виникала, коли в назві торгової точки були подвійні лапки, а термінал відповідав за протоколом PosApi/JSON. Дрібниця в довіднику ламала розбір відповіді терміналу. За спостереженнями, такі кейси живучі: хтось перейменував точку на «Магазин “Центр”», і вечеря п’ятниці на касі перетворюється на квест.

Чому парсер такий прискіпливий

JSON описаний у RFC 8259 (грудень 2017, rfc-editor.org). Формат навмисно тісний. Подвійні лапки для ключів і рядків. Без коментарів. Без кінцевої коми після останнього поля. Без одинарних лапок. Числа без ведучого нуля. Для обміну між системами — UTF-8. Байтову мітку порядку (BOM, символ U+FEFF) у мережевий JSON додавати не можна; парсери можуть її ігнорувати, але не зобов’язані. Ось і вся «магія».

JavaScript-об’єкт і JSON — не близнюки. У коді можна написати {name: ‘Оля’, age: 20,}. У JSON — ні. Тут { “name”: “Оля”, “age”: 20 }. Кінцева кома — синтаксична помилка. Коментар // тимчасово — теж. Саме тому «я скопіював з консолі Node і вставив у файл» іноді вибухає. Консоль друкує JS, а не JSON.

HTTP додає другий шар хаосу. Заголовок Content-Type: application/json ніхто не зобов’язаний дотримуватися ідеально. Шлюз може віддати text/html; charset=UTF-8 і сторінку «502 Bad Gateway». fetch().json() цього не прощає. Axios за замовчуванням теж намагається розібрати JSON і видає малозрозумілий fail. Якщо не логувати сире тіло — будете тиждень шукати «баг у моделі», якого немає.

Пишете так JSON каже Що зробити
{“a”: 1,} Кінцева кома заборонена Прибрати кому після останнього поля
{‘a’: 1} Одинарні лапки не є рядком JSON Лише подвійні лапки
{a: 1} Ключ має бути в лапках “a”: 1
{“a”: 01} Ведучий нуль у числі невалідний 1 або рядок “01”
{“msg”: “Привітn} Рядок не закритий або сирий перенос Екранувати n, закрити лапки
// коментар {“a”:1} Коментарів у стандарті немає Прибрати або тримати JSONC окремо

Ця таблиця рятує від півгодини тикання в JSONLint. Якщо відповідь «майже правильна», парсеру байдуже. Він не додумує. І це добре: інакше два клієнти зібрали б різні об’єкти з одного й того ж рядка.

Найчастіші причини

HTML замість JSON

Перший символ тіла — <. Далі !DOCTYPE або html. Клієнт чекав {, отримав сторінку. Типові джерела: неіснуючий URL API, редірект на логін, сторінка 404/500 веб-сервера, WAF-заглушка, відповідь nginx «Welcome to nginx». У консолі це якраз Unexpected token < in JSON at position 0.

Перевірка займає хвилину. DevTools → Network → ваш запит → вкладка Response. Побачили верстку — шукайте, чому запит не потрапив на правильний endpoint. Часто винна зайва риска в base URL, змішаний http/https, або фронтенд б’є не в /api/orders, а в /orders і отримує HTML роутера.

Порожнє або обрізане тіло

Unexpected end of JSON input. Відповідь обірвалася на півслові: {“ok”: tr. Або прийшов порожній рядок після 204 No Content, а код усе одно викликає .json(). Або таймаут посередині, проксі відрізав пакет, мобільна мережа моргнула.

Тут уже мережа винна частіше, ніж синтаксис. Але зовнішній симптом той самий — парсер не зібрав значення. Дивіться Content-Length, чи збігається він із фактичним розміром, чи немає обриву keep-alive, чи не стоїть занадто короткий timeout на клієнті.

Лапки, коми, «ручний» JSON

Найлюдяніша причина. Хтось склеїв JSON рядками: ‘{“name”: “‘ + userName + ‘”}’. У name з’явилися лапки, зворотна риска або символ нового рядка — і все. Штатний JSON.stringify / json.dumps / json_encode цього не допустить, бо екранує сам. «Ручний» конкатенат — ні.

Помічав це в інтеграціях з 1С/BAS і в самописних обмінах «файл туди-сюди». Людина відкрила JSON у Блокноті, поставила кому «для краси», зберегла в Windows-1251. Наступного ранку парсер на Linux читає кракозябри й падає на першому байті. Не містика. Кодування.

Кодування, BOM, проксі, VPN, антивірус

UTF-8 без BOM — норма для мережевого JSON. Файл, збережений «з підписом UTF-8» у Windows, починається з байтів EF BB BF. Деякі парсери це проковтнуть, деякі — ні. Старі обміни українських облікових систем інколи віддають Windows-1251, а клієнт чекає UTF-8. Виглядає як «бінарне сміття» на початку тіла.

Проксі й VPN додають свої сюрпризи. Корпоративний HTTPS-inspection підміняє сертифікат і іноді вставляє HTML-заглушку. Антивірус на Windows перехоплює трафік. Мобільний оператор «оптимізує» сторінки. На iOS це легко доїжджає до NSURLErrorCannotParseResponse. Вимкнули VPN — запрацювало. Висновок не «VPN зламаний», а «між вами і API з’явився ще один HTTP-клієнт зі своєю думкою про формат».

Чат-боти й відповіді моделей

Модель просять «верни лише JSON», а вона чемно відповідає: звісно, ось “`json { … } “`. Парсер бачить зворотну риску й літеру, не дужку. Або після валідного об’єкта дописує речення «Сподіваюсь, це допоможе». Для JSON це вже «extra data after JSON».

У Telegram окрема історія з parse_mode. Бот шле Markdown, а в тексті є символи _, *, [, і клієнт Telegram не може розібрати розмітку. Це теж «не вдалося розпарсити», тільки вже не ваш JSON, а розмітка повідомлення. Лікується екрануванням або parse_mode HTML з акуратним escape, не «ще раз надіслати те саме».

Як діагностувати за п’ять хвилин

Не треба одразу дебагер на годину. Потрібен сирий слід. Нижче — порядок, яким я сам користуюся, коли мені кидають скрін з цим текстом і нічого більше.

  1. Зафіксуйте час, екран, браузер/додаток, Wi-Fi чи мобільний інтернет, чи був VPN. Без цього підтримка гратиме в угадайку.
  2. Повторіть дію в режимі інкогніто або в іншому браузері. Якщо там працює — кеш, розширення, перехоплювач трафіку.
  3. Відкрийте DevTools (F12) → Network. Знайдіть запит, який падає. Подивіться Status, Content-Type, вкладки Headers і Response.
  4. Подивіться перший символ тіла. Від нього залежить майже вся гіпотеза.
  5. Якщо це десктопна каса чи облікова система — знайдіть лог поруч із програмою. Шукайте те саме повідомлення і рядок відповіді, який не змогли прочитати.
  6. Зі смартфона: інша мережа (Wi-Fi ↔ мобільний), без VPN, оновлення додатка. Якщо не допомогло — справа на сервері, не в «прошивці телефону».

Цей порядок нудний. Він зате відсікає 80% розмов «у мене зламався інтернет». Якщо в Response вже видно HTML логіну — ви не будете чистити cookies тричі. Якщо тіло обірване — підете до шлюзу, не до JSONLint.

Шпаргалка по першому символу. Тримати під рукою зручніше, ніж пам’ятати напам’ять усі SyntaxError з MDN.

  • { або [ — це схоже на JSON. Шукайте кінцеву кому, одинарні лапки, сирий перенос, обрив у кінці.
  • < — HTML. Дивіться статус, URL, редіректи, WAF, сторінку логіну.
  • Порожньо — 204, таймаут, порожній body. Не викликайте .json() на порожнечі.
  • % — іноді PDF або URL-encoded сміття. Не той endpoint.
  • Зворотна риска й слово json — markdown-огорожа від моделі або чату.
  • Кракозябри на кшталт РџСЂРёРІ — кодування не UTF-8.

Після списку завжди те саме правило: спочатку тіло, потім теорії. Теорій у цієї помилки багато. Тіло одне.

Якщо ви звичайний користувач

Вам не потрібен Postman. Потрібно зрозуміти, чи це «у всіх», чи тільки у вас, і не зробити гірше. Особливо якщо мова про оплату, відправку заявки, фіскальний чек.

  1. Оновіть сторінку один раз. Не десять. Якщо це була оплата — спочатку перевірте кабінет/смс/виписку, чи не пройшов платіж.
  2. Відкрийте те саме в режимі інкогніто. Вимкніть VPN. Спробуйте мобільний інтернет замість Wi-Fi або навпаки.
  3. Очистіть дані саме цього сайту (іконка замка біля адреси → файли cookie), а не «весь кеш за весь час».
  4. Перевірте дату й час на пристрої. Збитий годинник ламає TLS, а зламаний TLS інколи доїжджає до вас уже як «незрозуміла відповідь».
  5. Оновіть додаток із офіційної крамниці. Старий клієнт може чекати старий формат API.
  6. Якщо нічого не змінилось — пишіть у підтримку зі скріном, часом і назвою дії. Не «нічого не працює», а «кнопка Оплатити, 09:14, Chrome, помилка розбору відповіді».

Цього достатньо, щоб відсіяти локальний мотлох. Якщо в інкогніто без VPN усе відкривається, шукайте розширення браузера, «прискорювачі» інтернету, корпоративний антивірус. Якщо ніде не відкривається — проблема на боці сервісу, і ваша задача — не лагодити JSON, а дати підтримці слід.

Окремо про платежі й відправку форм. Парсер міг упасти вже після того, як сервер усе зберіг. Повторний клік створює другий платіж, другу заявку, другий чек. Спочатку статус операції. Потім повтор.

Якщо ви розробник або адмін

Тут уже не інкогніто, а дисципліна. Більшість «містичних» падінь зникає, щойно ви перестаєте парсити відповідь наосліп.

  1. Не викликайте JSON.parse / response.json() / axios-автопарсинг, доки не перевірили status і Content-Type.
  2. Для 204 і порожнього body повертайте null самі, не змушуйте парсер їсти порожнечу.
  3. У catch логуйте перші 200–500 символів тіла, статус, URL, correlation-id. Не весь payload із токенами й картками.
  4. Збирайте JSON штатними серіалізаторами. Жодного склеювання рядків із даними користувача.
  5. На шлюзі (nginx, Traefik, CDN) помилкові сторінки для /api краще віддавати як JSON, не як HTML-заглушку «502 Bad Gateway».
  6. Перевіряйте кодування: UTF-8 без BOM. PHP-попередження, BOM і echo перед json_encode — класика, яку видно за першим символом.
  7. Для відповідей моделей: спочатку витягнути JSON з огорожі, потім парсити, потім валідувати схемою (Zod, Pydantic, JSON Schema). Парсинг і валідація — різні кроки.

Мінімальний кістяк на фронті виглядає так. Спочатку res.ok і content-type. Потім text(). Потім спроба JSON.parse у try/catch. Якщо спіймали SyntaxError — у лог іде прев’ю рядка, не мовчазний «parse failed». Користувачу показуєте людське «сервер повернув незрозумілу відповідь, ми вже бачимо слід», а не стек.

Інструменти, які реально допомагають, а не «ще один дашборд»: вкладка Network у Chrome/Firefox, Postman або Insomnia для відтворення, curl -i щоб побачити заголовки, jq для швидкої перевірки валідності, JSONLint коли треба тикнути пальцем у кому. На сервері — access-лог поруч із телом помилки, не окремо через три системи.

Безпека логів. У практиці бачив дамп «для дебагу», де в перших 2 КБ відповіді сидів Bearer-токен і фрагмент ПІБ. Парсинг упав, токен поїхав у Sentry. Ріжте тіло. Маскуйте Authorization, cookie, iban, номер картки. Помилка парсингу — не дозвіл логірувати все підряд.

Чого не варто робити

Є ритуали, які лише розтягують ремонт. Вони заспокоюють, бо «я щось роблю». На причину не впливають.

  • Перевстановлювати Windows, «чистити реєстр», ставити інший антивірус «на всяк випадок».
  • Бити «Повторити» десять разів на формі оплати чи відправки чека.
  • Чистити весь кеш браузера разом із паролями, якщо достатньо даних одного сайту.
  • Міняти DNS, MTU і «прошивку роутера» до того, як ви подивилися тіло відповіді.
  • Вважати, що «в Chrome працює, отже Safari зламаний» — WebKit інколи суворіший, але частіше просто інший кеш або ITP ріже cookie, і API відповідає HTML логіну.
  • Шукати вірус, бо повідомлення українською і звучить загрозливо. Парсер — не антивірус.

Якщо після інкогніто, іншої мережі й одного оновлення нічого не змінилось — зупиніться. Далі або лог/Network, або лист у підтримку. Третій шар «народних» дій майже ніколи не лікує розбір JSON.

Короткі відповіді на типові питання

Це вірус або злам? Ні, саме по собі ні. Це збій розбору формату. Злам теоретично може підмінити відповідь на HTML-фішинг, але тоді ви побачите чужу верстку в Response, не «невидимий вірус». Починати з антивіруса — втрата часу.

Чому на комп’ютері є, а в телефоні немає? Різний клієнт, різний кеш, інколи різний шлюз (мобільний оператор любить втручатися). Або додаток зібраний зі старою схемою відповіді. Порівнюйте не «пристрій поганий», а тіло відповіді на обох.

Можна натиснути ще раз? Залежить від дії. Читання списку замовлень — так. Оплата, відправка декларації, фіскалізація чека — спочатку статус операції. Парсер міг упасти після успішного запису на сервері.

Чому з’явилось після оновлення? Сервер змінив контракт: поле стало масивом, обгортка data з’явилась, помилки тепер HTML від нового CDN. Старий клієнт чекає старе. Або, як у кейсі з терміналом, у довідник потрапив символ, якого серіалізатор раніше не зустрічав.

Чи допоможе інший браузер? Як тест — так. Як лікування — лише якщо винне розширення чи кеш. Якщо API віддає битий JSON, браузер ні при чому. Safari інколи раніше показує NSURLErrorCannotParseResponse на дивних шлюзах, Chrome може бути терплячішим до того самого пакета. Це аргумент подивитися мережу, не «перейти на Chrome назавжди».

І ще одне, бо питають. Помилка парсингу не лікується «більшим тарифом інтернету». Розмір відповіді тут ні до чого, поки тіло не обрізане. Якщо відповідь на 800 байт і починається з <html endpoint.

Що зробити прямо зараз

Відкрийте ту саму дію ще раз, але вже зі слідством, не зі страхом. Інкогніто, без VPN, один повтор. Якщо це оплата — перевірте, чи вона вже пройшла. Якщо маєте доступ до DevTools або логу — знайдіть перший символ тіла й зіставте з шпаргалкою вище. HTML, порожнеча, битий JSON і markdown-огорожа лікуються по-різному, але жодна з них не вимагає перевстановлювати систему.

Розробникам: поставте перевірку статусу й прев’ю тіла до JSON.parse сьогодні, не «коли буде час». Користувачам: надішліть підтримці час, пристрій і скрін, а не загадку. Парсер мовчить не тому, що світ складний. Він просто не читає текст, який йому підсунули замість домовленого формату. Покажіть йому правильний рядок — і повідомлення «не вдалося розпарсити відповідь» зникне без ритуалів.

“`

Лісова Софія

Софія Лісова — авторка матеріалів з Python, Data Science та аналітики даних. Має досвід роботи Data Analyst і ML Engineer, викладає понад 6 років. Пише про Pandas, машинне навчання, візуалізацію даних і кар’єру в аналітиці. Її тексти відзначаються чіткістю, структурованістю та великою кількістю практичних ноутбуків. У Main Academy Софія відповідає за контент, пов’язаний з Python і Data-напрямками. Вважає, що «дані — це нова нафта, але лише якщо вмієш їх правильно видобувати і аналізувати».

More From Author

Значення імені Артем: неушкоджений і трохи впертий

Leave a Reply

Your email address will not be published. Required fields are marked *