zod
zod copied to clipboard
Preprocessing hides errors in zod 3.22.x
Consider this piece of code:
import { z } from "zod";
const zodStringWithPreprocess =
// Notice that in this simplified example, the preprocessing does nothing
z.preprocess((val) => val, z.string()) as z.ZodEffects<
z.ZodString,
string,
string
>;
const validatorWithoutPreprocess = z.object({
name: z.string(),
town: z.string(),
});
const validatorWithPreprocess = z.object({
name: zodStringWithPreprocess,
town: zodStringWithPreprocess,
});
const dataToValidate = {};
const parsedWithoutPreprocess =
validatorWithoutPreprocess.safeParse(dataToValidate);
console.log("Without preprocess");
console.log(JSON.stringify(parsedWithoutPreprocess, null, 2));
const parsedWithPreprocess = validatorWithPreprocess.safeParse(dataToValidate);
console.log("With preprocess");
console.log(JSON.stringify(parsedWithPreprocess, null, 2));
Expected behavior
In Zod 3.21.4, the result is the same regardless of preprocessing:
Without preprocess
{
"success": false,
"error": {
"issues": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"name"
],
"message": "Required"
},
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"town"
],
"message": "Required"
}
],
"name": "ZodError"
}
}
With preprocess
{
"success": false,
"error": {
"issues": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"name"
],
"message": "Required"
},
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"town"
],
"message": "Required"
}
],
"name": "ZodError"
}
}
Actual behavior
In Zod 3.22.x (x <= 2), the version with preprocessing stops at the first error:
Without preprocess
{
"success": false,
"error": {
"issues": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"name"
],
"message": "Required"
},
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"town"
],
"message": "Required"
}
],
"name": "ZodError"
}
}
With preprocess
{
"success": false,
"error": {
"issues": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"name"
],
"message": "Required"
}
],
"name": "ZodError"
}
}
Notice that the "expected": "string",
for the town
property is missing.
It seems to be a regression for Zod.
Hi there, I'm observing a similar issue. With Zod 3.22.0+ one of the issues is missing from the object, with Zod 3.21.4 it is there as expected. We use a Regex against the stringified error, this is where we noticed it being missing. Below our results from testing:
[
// Zod 3.21.4
{
"details": {
"parameterErrors": [
{
"message": "Expected date, received boolean",
"type": "invalid_type",
"path": ["contracts", 2, "signup_date"]
},
{
"message": "Expected date, received boolean",
"type": "invalid_type",
"path": ["contracts", 2, "end_date"]
}
]
}
},
// Zod.3.22.2
{
"details": {
"parameterErrors": [
{
"message": "Expected date, received boolean",
"type": "invalid_type",
"path": ["contracts", 1, "signup_date"]
}
]
}
}
]
As far as I can tell this was fixed in zod 3.23.0. I have a test case for this issue in my project which passes with 3.21, fails with 3.22 and now passes again with 3.23 😅