Chasing a Ghost Bug After Upgrading to NextJS 16
November 9, 2025
I'd like to share my recent experience debugging our web app at Needle.
We recently updated to Next.js 16, and soon after, we started seeing new issues in the backend. Things I’d never seen before, something completely out of the blue.
Be prepared to listen to a tale of a ghost bug.

Our app has been running on NextJS 14 for a while now, so we had plenty of reasons to upgrade and enjoy all the new features from both versions 15 and 16. Plus I don't like to let tech debt grow for a long time.
I did the migration myself (the AI agent–based migration totally did not work) and started testing the app locally. Everything was silky smooth. I also loved the new development experience. Those fast hot reloads are one of the main reasons we decided to adopt NextJS 16 so early.
It flew beautifully locally, but once deployed to production… smoke started coming out. You know the classic line: It works on my machine right?
We started seeing this weird JSON parse error on many API endpoints. Request body was not being parsed correctly.
SyntaxError: Unexpected token 'e', "eedle, deb"... is not valid JSON
at JSON.parse (<anonymous>)
at parseJSONFromBytes (node:internal/deps/undici/undici:5738:19)
at successSteps (node:internal/deps/undici/undici:5719:27)
at fullyReadBody (node:internal/deps/undici/undici:4609:9)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async consumeBody (node:internal/deps/undici/undici:5728:7)
at async /home/bun/app/.next/server/chunks/needle-web_4b681e8d._.js:1:1433
at async Object.read (/home/bun/app/.next/server/chunks/needle-web_4b681e8d._.js:1:1068)
at async Object.getRawInput (/home/bun/app/.next/server/chunks/needle-web_4b681e8d._.js:1:1943)
at async i (/home/bun/app/.next/server/chunks/needle-web_4b681e8d._.js:5:7159)Since NextJS 16 was just released and given how the community tends to treat new NextJS versions as “next-stable” 😄 I was biased into thinking this issue must be related to the new version.
The Chase
I tried many many many things to pinpoint the root cause. Here’s what I remember (though the full list was definitely longer 😅):
- Build for prod, run locally → everything works.
- Build Docker image, run locally → everything works.
- The issue seemed transient — not always happening, but frequently enough to look almost persistent.
- Try different API endpoints to spot a pattern → some endpoints were less flaky, others more. Seems like when the request body is large, error rate goes up.
- Make requests with different clients:
curl, browser,fetch→ same story. - Enforce HTTP/1.1 to see if it's an HTTP/2 issue.
- Make requests from a bastion VM in GCP → issue does not happen there at all.
- Add extra logging in the backend.
- Finally, follow the error stacktrace from the production... SSH into the VM, drill down into the built NextJS files (yes, those minified cryptic JavaScript ones 😬), and see exactly which statement caused the problem →
NextRequest.json().


So the issue was happening before any of our own code ran, something at the infra level.
At this point so far, I've had some solid patience test. I actually felt some personal growth going through this chase.
I took a deep breath and decided to simplify the signals.
The Signals
- Started happening after the NextJS 16 migration.
- Transient issue.
- Infra-level code.
Hmm. Nothing changed in GCP, cannot be load balancer. So rule that out.
Some of our code changed, but it had to be something at the infra level.
I went over the entire PR for the NextJS migration and spotted a few places that impacted infra-level behavior.
Within about 15 minutes, I found the culprit.
The Culprit
Ladies and gentlemen, do not use the nodejs runtime for your proxy (middleware) when deploying NextJS in standalone mode.
In NextJS 14, middleware ran strictly in the edge runtime, whereas in NextJS 15 and 16, there’s an additional supported runtime: nodejs. So I thought — it makes natural sense to use nodejs for an app that’s running without any explicit edge configuration.
But that change caused request bodies to be truncated, which in turn led to the bizarre JSON parse failures I’d been chasing.
I’m not too comfortable sharing how long this battle took me 😅 but I hope you’ve been in a similar place before and can relate.
As of now, I don't know the exact behaviour difference (and the actual root cause) between these two run times when deploying NextJS in standalone mode. I hope we'll get more documentation on that. For the time being, I submmited an issue for this on NextJS Github repository. Follow the discussion here .
Thanks for reading, my fellow curious visitor — cheers till next time!