Skip to content

fix: correct isJSONSerializable to handle null without throwing#572

Open
EduardF1 wants to merge 1 commit into
unjs:mainfrom
EduardF1:fix/isJSONSerializable-null
Open

fix: correct isJSONSerializable to handle null without throwing#572
EduardF1 wants to merge 1 commit into
unjs:mainfrom
EduardF1:fix/isJSONSerializable-null

Conversation

@EduardF1

@EduardF1 EduardF1 commented May 4, 2026

Copy link
Copy Markdown

Problem

isJSONSerializable(null) throws a TypeError and incorrectly returns
alse.

Root cause

// src/utils.ts
const t = typeof value;
if (t === "string" || t === "number" || t === "boolean" || t === null) {
  return true;
}
// ...
if (value.buffer) { // <-- throws TypeError when value is null

Two bugs on the same line:

  1. Dead code: ypeof value never returns "null" — it returns "object" for
    ull. So === null can never be rue.
  2. Throws on null: Since the early-return check is never triggered for
    ull, execution continues to
    alue.buffer, which throws TypeError: Cannot read properties of null (reading 'buffer').

Verified:
ull is valid JSON (JSON.stringify(null) → "null"), so the function should return rue for it.

Fix

Replace === null with
alue === null:

// Before
if (t === "string" || t === "number" || t === "boolean" || t === null) {

// After
if (t === "string" || t === "number" || t === "boolean" || value === null) {

This correctly checks the value itself rather than its ypeof result, so
ull hits the early
eturn true before reaching
alue.buffer.

Fixes #571

@magion33

magion33 commented May 5, 2026

Copy link
Copy Markdown

@EduardF1 I'm the creator of the issue and this is exactly what fix I had in mind. LGTM

Note: Another partially related thing is that the body serialization logic of the fetch here

    if (context.options.body && isPayloadMethod(context.options.method)) {
      if (isJSONSerializable(context.options.body)) {
         ...

doesn't allow to have a body of "" (empty string), or 0, or null. All of those are technically valid JSON-serializable values and should be allowed imo.

@EduardF1

EduardF1 commented May 5, 2026

Copy link
Copy Markdown
Author

@magion33 thanks for the LGTM, and good catch on the body-serialization gap. Looking at https://github.com/unjs/ofetch/blob/main/src/fetch.ts#L130:

if (context.options.body && isPayloadMethod(context.options.method)) {

The leading context.options.body && is the part that drops "", 0, and null before isJSONSerializable is even consulted. Replacing it with an explicit `!== undefined` check (and handling those primitives in the stringify branch) is the right fix, but it's a separate concern from #571 — different file, different surface area, different test coverage.

I'd prefer to keep this PR narrowly focused on the dead-code/throws-on-null bug so it's easy for maintainers to review and merge, and then land the body-serialization fix as a follow-up that references this one. Happy to open that follow-up PR right after this merges (or in parallel if a maintainer signals it's fine to bundle).

@magion33

magion33 commented May 5, 2026

Copy link
Copy Markdown

@EduardF1 Yes, I agree. It's a different topic, just slightly related.

@EduardF1

Copy link
Copy Markdown
Author

Summary

Fixes isJSONSerializable(null) throwing instead of returning false, preventing unintended errors when null is passed as a request body (fixes #571).

Status

  • CI: ✅ All checks passing
  • Issue reporter (@magion33) confirmed this matches their intent
  • Two-line fix in utils.ts with test coverage for the null path
  • Ready for review

@pi0 happy to adjust scope or naming if needed. Thanks!

@EduardF1

Copy link
Copy Markdown
Author

Thanks again @magion33 for validating the fix and for agreeing to keep the body-serialization case separate. I am happy to send that follow-up on its own after this one lands if maintainers want it.

@EduardF1

EduardF1 commented Jun 4, 2026

Copy link
Copy Markdown
Author

Hi team, checking in on this PR! Let me know if you have any questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

isJSONSerializable bug

2 participants