Skip to content
honojs/hono #4707TypeScript

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

  1. Warpway replay

    Flags it, given only what existed before merge

  2. 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
Language
TypeScript

What Warpway flags in the replay

Warpway reviewReplayed 2026-10-07#4707 perf(context): add fast path to c.json() matching c.text() optimization
  • CorrectnessIncomplete
  • SecurityCleared
  • Testing / ReliabilityIssue found
  • Architecture / MaintainabilityIncomplete
  • Backward CompatibilityIncomplete
  • PerformanceCleared
  • Product SemanticsIssue found
  1. 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

src/context.ts (4.12.0)
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:

ts
app.get('/user', (c) => c.json(findUser(c.req.query('id')))); // findUser returns undefined
text
TypeError: Value is not JSON serializable

The 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

  1. Opened

    #4707 opened

  2. Release

    Hono 4.12.0 ships the fast path

  3. 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

  1. 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.

  2. 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.

  3. 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.