mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
fix: Response.json() throws TypeError for non-JSON serializable top-level values (#21258)
Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Meghan Denny <meghan@bun.com>
This commit is contained in:
@@ -515,6 +515,17 @@ pub fn constructJSON(
|
||||
const json_value = args.nextEat() orelse jsc.JSValue.zero;
|
||||
|
||||
if (@intFromEnum(json_value) != 0) {
|
||||
// Validate top-level values that are not JSON serializable (Node.js compatibility)
|
||||
if (json_value.isUndefined() or json_value.isSymbol() or json_value.jsType() == .JSFunction) {
|
||||
const err = globalThis.createTypeErrorInstance("Value is not JSON serializable", .{});
|
||||
return globalThis.throwValue(err);
|
||||
}
|
||||
|
||||
// BigInt has a different error message to match Node.js exactly
|
||||
if (json_value.isBigInt()) {
|
||||
const err = globalThis.createTypeErrorInstance("Do not know how to serialize a BigInt", .{});
|
||||
return globalThis.throwValue(err);
|
||||
}
|
||||
var str = bun.String.empty;
|
||||
// calling JSON.stringify on an empty string adds extra quotes
|
||||
// so this is correct
|
||||
|
||||
@@ -1172,6 +1172,30 @@ describe("Response", () => {
|
||||
expect(response.headers.get("x-hello")).toBe("world");
|
||||
expect(response.status).toBe(408);
|
||||
});
|
||||
|
||||
it("throws TypeError for non-JSON serializable top-level values (Node.js compatibility)", () => {
|
||||
// Symbol, Function, and undefined should throw "Value is not JSON serializable"
|
||||
expect(() => Response.json(Symbol("test"))).toThrow("Value is not JSON serializable");
|
||||
expect(() => Response.json(function () {})).toThrow("Value is not JSON serializable");
|
||||
expect(() => Response.json(undefined)).toThrow("Value is not JSON serializable");
|
||||
|
||||
// These should not throw (valid values)
|
||||
expect(() => Response.json(null)).not.toThrow();
|
||||
expect(() => Response.json({})).not.toThrow();
|
||||
expect(() => Response.json("string")).not.toThrow();
|
||||
expect(() => Response.json(123)).not.toThrow();
|
||||
expect(() => Response.json(true)).not.toThrow();
|
||||
expect(() => Response.json([1, 2, 3])).not.toThrow();
|
||||
|
||||
// Objects containing non-serializable values should not throw at top-level
|
||||
// (they get filtered out by JSON.stringify)
|
||||
expect(() => Response.json({ symbol: Symbol("test") })).not.toThrow();
|
||||
expect(() => Response.json({ func: function () {} })).not.toThrow();
|
||||
expect(() => Response.json({ undef: undefined })).not.toThrow();
|
||||
|
||||
// BigInt should throw with Node.js compatible error message
|
||||
expect(() => Response.json(123n)).toThrow("Do not know how to serialize a BigInt");
|
||||
});
|
||||
});
|
||||
describe("Response.redirect", () => {
|
||||
it("works", () => {
|
||||
|
||||
32
test/regression/issue/21257.test.ts
Normal file
32
test/regression/issue/21257.test.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
// Regression test for GitHub Issue #21257
|
||||
// https://github.com/oven-sh/bun/issues/21257
|
||||
// `Response.json()` should throw with top level value of `function` `symbol` `undefined` (node compatibility)
|
||||
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
test("Response.json() throws TypeError for non-JSON serializable top-level values", () => {
|
||||
// These should throw "Value is not JSON serializable"
|
||||
expect(() => Response.json(Symbol("test"))).toThrow("Value is not JSON serializable");
|
||||
expect(() => Response.json(function testFunc() {})).toThrow("Value is not JSON serializable");
|
||||
expect(() => Response.json(undefined)).toThrow("Value is not JSON serializable");
|
||||
});
|
||||
|
||||
test("Response.json() works correctly with valid values", () => {
|
||||
// These should not throw
|
||||
expect(() => Response.json(null)).not.toThrow();
|
||||
expect(() => Response.json({})).not.toThrow();
|
||||
expect(() => Response.json("string")).not.toThrow();
|
||||
expect(() => Response.json(123)).not.toThrow();
|
||||
expect(() => Response.json(true)).not.toThrow();
|
||||
expect(() => Response.json([1, 2, 3])).not.toThrow();
|
||||
|
||||
// Objects containing non-serializable values should not throw at top-level
|
||||
expect(() => Response.json({ symbol: Symbol("test") })).not.toThrow();
|
||||
expect(() => Response.json({ func: function () {} })).not.toThrow();
|
||||
expect(() => Response.json({ undef: undefined })).not.toThrow();
|
||||
});
|
||||
|
||||
test("Response.json() BigInt error matches Node.js", () => {
|
||||
// BigInt should throw with Node.js compatible error message
|
||||
expect(() => Response.json(123n)).toThrow("Do not know how to serialize a BigInt");
|
||||
});
|
||||
Reference in New Issue
Block a user