Skip to content

Build1 publisher2 min readPublished

V8's 'is not valid JSON' error quotes up to 20 characters of what the server sent

V8's 'is not valid JSON' error quotes the whole response body when it is 20 characters or fewer, according to a developer's tests on Node 24.13. A quote without a trailing ellipsis is the entire reply, so a short error body can be diagnosed from the logged message alone.

The Engineer · Build desk

Illustration accompanying V8's 'is not valid JSON' error quotes up to 20 characters of what the server sent

What happened

  • Past 20 characters, the quote ends ten characters beyond where parsing failed and is capped at 20 characters wide, sliding forward once it reaches the cap.
  • Messages without the 'Unexpected token' clause, such as "undefined" is not valid JSON, mean nothing came off the wire, according to the post's author.
  • A Next.js 16 dev server answered an Accept: application/json request for a missing API route with a 26,283-byte HTML 404 page, and Vercel production sent 20,672 bytes.
  • In four of the author's five test targets fetch never threw; every exception came from res.json(), with status and Content-Type already available.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure Code that treats a 200 as success is exposed to expired sessions: fetch follows the redirect to a login page silently, and only res.url or res.redirected shows the detour.
  • constraint For long HTML bodies the quote shows only the first ten characters. The status and final URL that explain the failure have to come from the response object.
  • constraint Alerting keyed on network-failure text has to match both Node's 'fetch failed' and Chrome 152's 'Failed to fetch' to catch the same missing response.

The author found the threshold by brute force. On Node v24.13.0 with V8 13.6.233.17 [16], a loop passed `JSON.parse` a string beginning with `<` and grew it one character at a time [3]. At 20 characters the message quoted all of it: `"<xxxxxxxxxxxxxxxxxxx" is not valid JSON`. At 21 it printed `"<xxxxxxxxx"...` [3]. The quoted text is the input itself, copied from the string the parser was handed [1].

A second loop showed where the ten characters are counted from. It opened an array with `[`, added spaces, then junk, so the parser accepted a few characters before it failed [6]. The quote grew with the failure point: 11 characters at index 1, 12 at index 2, 13 at index 3, 16 at index 6, and 20 at index 11 [6]. Past that, the window slid forward at a fixed width [6]. The same rule explains an error that quotes eleven characters, or a run of text from the middle of a payload [15]. An HTML body fails on `<` at index 0, so it always gets its first ten characters [14].

Ten characters is a thin sample of a long reply. For the Next.js dev server's 404 page, the error line showed about 0.04 percent of the body [1]. The author argues ten characters is enough to identify most things that turn up where JSON should be [18]. For short replies the claim is stronger. "A short body is fully diagnosed from the error line alone, without opening DevTools," the author wrote [17].

All of these parser numbers come from one build [16]. The post's parser tests ran on that build alone, so the 20-character cap and the ten-character lead are observed behaviour of V8 13.6.233.17. A log rule that treats a missing ellipsis as proof of a complete body depends on the deployed runtime keeping that format.

The author's fix puts the evidence into the exception before the parser runs [13]. The helper reads `const body = await res.text(); // text first, not json`, then tests `if (!type.includes('application/json'))` [13]. On a miss it throws with the status, the Content-Type, the final URL flagged `(redirected)` when fetch followed one, and the first 200 characters of the body [13]. That is twenty times the window V8 gives an HTML response [2]. I think this is the right default for a shared fetch helper. The added code is one header check and one string slice [13].

What to watch

  • Whether the 20-character cap and ten-character lead hold on V8 builds after 13.6.233.17, and what other JavaScript engines print for the same failed parse.
  • Whether Next.js starts answering Accept: application/json on missing API routes with a JSON 404 body.
  • Whether Node and Chrome converge on one error string for a fetch that gets no response.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories