Skip to content

no-json-stringify-error

Avoid generic JSON serialization that can omit native Error details.

Why

Native Error details are non-enumerable, so generic JSON serialization discards diagnostic information.

Fix

Serialize explicit error fields or use an error-aware serializer.

Examples

Before — flagged A computed prototype name remains ordinary serialized data
src/error.js
JSON.stringify({
error: new Error("lost"),
["__proto__"]: {
toJSON() {
return { message: "kept" };
},
},
});
After — preferred A literal prototype hook owns the payload serializer contract
src/error.js
JSON.stringify({
error: new Error("lost"),
__proto__: {
toJSON() {
return { message: "kept" };
},
},
});
Before — flagged A later write does not change an already constructed native Error
src/error.js
const value = new Error("lost");
Error = class Other {
message = "kept";
};
JSON.stringify(value);
After — preferred A replaced constructor does not prove a native Error
src/error.js
Error = class Other {
message = "kept";
};
const value = new Error();
JSON.stringify(value);
Before — flagged Keep native binding provenance
src/error.ts
function probe() {
try {
throw { id: 1 };
} catch (err) {
if (err instanceof Error) return err.message;
{
const err = new Error("lost");
return JSON.stringify(err);
}
}
}
console.log(probe());
After — preferred Keep native binding provenance
src/error.ts
function probe() {
try {
throw { id: 1 };
} catch (err) {
if (err instanceof Error) return err.message;
return JSON.stringify(err);
}
}
console.log(probe());
Before — flagged Do not stringify an Error object
src/report.ts
try {
f();
} catch (err) {
JSON.stringify({ error: err });
}
After — preferred Narrow an unknown catch value before selecting fields
src/report.ts
try {
f();
} catch (err) {
JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
}
Before — flagged A noncallable toJSON does not replace native serialization
src/error.js
JSON.stringify({ error: new Error("lost"), toJSON: 42 });
After — preferred An explicit callable toJSON owns its serialization contract
src/error.js
JSON.stringify({
error: new Error("lost"),
toJSON() {
return { message: "kept" };
},
});
Before — flagged A later write does not change an earlier native serialization
src/error.js
const result = JSON.stringify(new Error("lost"));
JSON.stringify = (value) => "kept:" + value.message;
After — preferred A replaced serializer owns its error-field contract
src/error.js
JSON.stringify = (value) => "kept:" + value.message;
JSON.stringify(new Error("lost"));
Before — flagged Serializing the whole Error still loses its native details
src/error.js
const error = new Error("lost", { cause: "kept" });
JSON.stringify(error);
After — preferred Native Error cause can be a string value
src/error.js
const error = new Error("lost", { cause: "kept" });
JSON.stringify(error.cause);
Before — flagged A constant computed serializer still loses native Error details
src/error.js
const key = "stringify";
JSON[key](new Error("lost"));
After — preferred A constant message field selects the explicit string escape hatch
src/error.js
const key = "message";
const error = new Error("lost");
JSON.stringify(error[key]);