Hono 4.12.0: c.json(undefined) throws "Value is not JSON serializable"
A Hono fast path for c.json() used Response.json(), which throws for undefined on Node. A Warpway replay of the PR flags it; it was reverted after four days.
Published 2 min readReplayed through Warpway on By the Warpway team
Replay vs. what happened
Warpway replay
Flags it, given only what existed before merge
Reported (#4756)
4 days after merge
Flagged: c.json(undefined) throws on the new fast path instead of returning a response
Replayed on 2026-10-07. Warpway was not installed on honojs/hono when this pull request was open.
Hono's c.json() builds a JSON response from any value. In February 2026, honojs/hono#4707 added a fast path matching an existing optimisation in c.text(): when there is no status and there are no extra headers, return Response.json(object) directly instead of stringifying the value and building the response by hand. It was a 14-line performance change, and it shipped in Hono 4.12.0 the same day.
On 2026-10-07 we replayed the pull request through Warpway as it stood before merge. The Testing lens reported that one call the repository explicitly supports, c.json(undefined), now throws. In reality, the fast path was reverted four days after merge.
Key facts
- Pull request
- honojs/hono#4707
- Merged
- 2026-02-19
- Shipped in
- 4.12.0
- Affected
- 4.12.0 to 4.12.1
- Fixed in
- 4.12.2 (revert #4757)
- Language
- TypeScript
What Warpway flags in the replay
- CorrectnessIncomplete
- SecurityCleared
- Testing / ReliabilityIssue found
- Architecture / MaintainabilityIncomplete
- Backward CompatibilityIncomplete
- PerformanceCleared
- Product SemanticsIssue found
- MediumTesting / Reliabilitysrc/context.ts:710
c.json(undefined) now throws on the fast path
With a fresh Context and no status or headers, c.json(undefined) takes the new Response.json(object) branch. Response.json rejects a value whose JSON serialization is undefined, so this call now throws instead of returning the previous empty-body Response with application/json. The repository explicitly type-tests c.json(undefined), and omitting the second argument is its tested form. A route returning an optional value that is undefined therefore fails rather than returning a response; passing an explicit status still uses the old path, making the behavior inconsistent.
Warpway saw the pull request as it stood before merge: final code, title, description and commits, without review comments or later history. Findings are quoted verbatim from the recorded review. Several lenses can report the same issue; Warpway merges them, and the first paragraph is shown.
The change
return this.#useFastPath() && !arg && !headers
? Response.json(object)
: this.#newResponse(JSON.stringify(object), arg, setDefaultContentType('application/json', headers));How the bug works
JSON.stringify(undefined) returns undefined, not a string. The old path tolerated that and returned an empty application/json response. Response.json(undefined) is stricter: the Fetch standard requires it to throw a TypeError when serialization produces nothing. On Node, a route like this fails:
app.get('/user', (c) => c.json(findUser(c.req.query('id')))); // findUser returns undefinedTypeError: Value is not JSON serializableThe behavior also depends on the runtime and on whether you passed a status: c.json(value, 200) still takes the old path and succeeds, so the same value works in one route and fails in the next.
Are you affected?
You are affected on Hono 4.12.0 and 4.12.1 if any route can call c.json() with undefined and no status or headers. On Node it returns a 500 with the error above; other runtimes behave differently, as described in #4756.
Fix or workaround
Upgrade to Hono 4.12.2 or later, which reverted the fast path. On an affected version, pass a status (c.json(value, 200)) or return null instead of undefined (the body is then the JSON null). We verified both on 4.12.0.
What happened next
Opened
#4707 opened
Merged
Release
Hono 4.12.0 ships the fast path
Warpway replay
Warpway replay: given the pull request as it stood before merge, it reports that c.json(undefined) now throws
Frequently asked questions
Why does c.json(undefined) throw "Value is not JSON serializable"?
In Hono 4.12.0 and 4.12.1, c.json() with no status or headers calls Response.json(value). Response.json throws a TypeError when JSON.stringify returns undefined, which it does for undefined. On Node the route fails with a 500.
How do I fix it?
Upgrade to Hono 4.12.2 or later. On 4.12.0 or 4.12.1, passing a status, as in c.json(value, 200), takes the old path and returns an empty application/json response. Returning null instead of undefined also avoids it.
Does it happen on Bun or Cloudflare Workers?
Behavior differs by runtime, which is why the fast path was reverted. The report in #4756 describes Bun returning an empty string and Cloudflare returning "undefined". We reproduced the Node behavior ourselves.
How we ran this
- 01
Replay the pull request as it stood before merge
Its final code, title, description and commits, and the repository at the fork point. No review comments, no bug reports, nothing after merge.
- 02
Review with Warpway’s default lenses
The production role models, lenses and default policy, run through Codex subscription access. One run per pull request, with no repository configuration.
- 03
Compare with what actually happened
Later fixes, reverts and bug reports in the public history, linked from every page so you can check our reading.
This pull request is one of 12 we replayed on 2026-10-07 from hono, axios and requests, including ones that were never fixed or reverted and the cases Warpway got wrong. Warpway was not installed on these repositories at the time; every review here is a replay. Older pull requests may appear in the model's training data, which is one reason we include recent ones and a bug nobody had reported. See every result from the pilot.
These replays used Codex subscription access rather than the production OpenAI API. They establish the recorded findings, not production API cost, general accuracy, customer time savings or an approval rate. The projects are open-source examples, not Warpway customers.
More case studies
- psf/requests #6667Python
requests 2.32 SSLContext regression: the wrong mTLS client certificate
- Warpway flags in a replay
- Client certificates leaking across requests through one shared SSLContext
- What happened
- Reported by users from 7 days after merge; reverted a year later in 2.32.5
- honojs/hono #5366TypeScript
Hono formData() after text() silently corrupts binary file uploads
- Warpway flags in a replay
- Binary file uploads silently altered when text() is read first
- What happened
- Not previously reported; we filed it as honojs/hono#5539
- honojs/hono #5133TypeScript
Hono cloneRawRequest: stale Content-Length after multipart re-encoding
- Warpway flags in a replay
- Forwarded multipart clones keep a Content-Length that no longer matches the body
- What happened
- Found independently 36 days after merge; fixed in 4.13.4