Skip to content

fix: correct isJSONSerializable to handle null without throwing#572

Open
EduardF1 wants to merge 2 commits into
unjs:mainfrom
EduardF1:fix/isJSONSerializable-null
Open

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

Conversation

@EduardF1

@EduardF1 EduardF1 commented May 4, 2026

Copy link
Copy Markdown
Contributor

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

Summary by CodeRabbit

  • Bug Fixes
    • Fixed JSON serializability checks so null is now correctly treated as a valid JSON value.
  • Tests
    • Added coverage to confirm null returns true in the JSON serializability check.

@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
Contributor 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
Contributor 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
Contributor 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
Contributor Author

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

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a6746c6-9dd8-47ae-8d82-5449c80f16d9

📥 Commits

Reviewing files that changed from the base of the PR and between dfbe3ca and a6cf69a.

📒 Files selected for processing (2)
  • src/utils.ts
  • test/index.test.ts

📝 Walkthrough

Walkthrough

Fixes a bug in isJSONSerializable where typeof value === null (always false) was used instead of value === null, causing null to not be recognized as JSON-serializable and throwing a TypeError. Adds a test asserting isJSONSerializable(null) returns true.

null serialization fix

Layer / File(s) Summary
Fix null check and add test coverage
src/utils.ts, test/index.test.ts
Replaces typeof value === null with value === null in isJSONSerializable; imports the function in the test suite and adds a case asserting isJSONSerializable(null) returns true.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Poem

🐇 A null walked in, was turned away,
typeof lied and caused dismay.
Now === null sets things right,
serializable null, shining bright!
No more TypeError in the night~ 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: handling null in isJSONSerializable without throwing.
Linked Issues check ✅ Passed The changes directly address #571 by making null JSON-serializable and adding a test for the null case.
Out of Scope Changes check ✅ Passed The PR stays narrowly scoped to the null-handling bug and its test coverage, with no unrelated changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.55%. Comparing base (27996d3) to head (a6cf69a).
⚠️ Report is 103 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #572       +/-   ##
===========================================
+ Coverage   56.86%   85.55%   +28.68%     
===========================================
  Files          16        5       -11     
  Lines         728      263      -465     
  Branches      113      133       +20     
===========================================
- Hits          414      225      -189     
+ Misses        303       33      -270     
+ Partials       11        5        -6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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