zod
zod copied to clipboard
ZodError#message cannot deal with circular data structures in its `issues`
Example
A Mocha test that shows the issue:
/* eslint-env mocha */
const { z, ZodError } = require('zod')
describe('zod', function () {
it('cannot deal with circular data structures', function () {
const AnObjectSchema = z.object({ someLiteralProperty: z.literal(1) })
const cicrularObject = {
aProperty: 'a property',
anotherProperty: 137,
anObjectProperty: { anObjectPropertyProperty: 'an object property property' },
anArrayProperty: [{ anArrayObjectPropertyProperty: 'an object property property' }]
}
cicrularObject.anObjectProperty.cicrularObject = cicrularObject
cicrularObject.anArrayProperty.push(cicrularObject.anObjectProperty)
const violatingObject = { someLiteralProperty: cicrularObject }
const { success, error } = AnObjectSchema.safeParse(violatingObject)
success.should.be.false()
error.should.be.an.instanceof(ZodError)
error.message.should.be.a.String()
})
})
This test fails with
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'anObjectProperty' -> object with constructor 'Object'
--- property 'cicrularObject' closes the circle
at JSON.stringify (<anonymous>)
at get message [as message] (node_modules/zod/lib/ZodError.js:105:21)
at Context.<anonymous> (test/00.zodIssue.js:23:11)
at process.processImmediate (node:internal/timers:478:21)
Analysis
ZodError.ts, line 283:
get message() {
return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);
}
JSON.stringify is called on this.issues. JSON.stringify cannot cope with circular data structures. In the example, the circular data structure appears in the received property of the issue.
jsonStringifyReplacer does not deal with this either:
helpers/util.ts#jsonStringifyReplacer, line 91:
export const jsonStringifyReplacer = (_: string, value: any): any => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
Good catch. For security reasons, Zod isn't supposed to print the input data when it throws an error...but you've found the exception here with ZodInvalidLiteralIssue. This will be fixed in Zod 4.