zod icon indicating copy to clipboard operation
zod copied to clipboard

zod.preprocess v3.22.2 issues are ignored in case of another error in the model

Open azhiv opened this issue 1 year ago • 6 comments

Another issue we found in v3.22.2 is that the schema inside preprocess either isn't executed or doesn't add issues to the context in case the model being parsed contains an error in another field (nonNullableObject below):

import * as zod from 'zod';

const wrapToArray = <T extends zod.ZodTypeAny>(elementSchema: T) =>
  zod.preprocess((val) => [val], zod.array(elementSchema));

const singleItemSchema = zod.object({
  quantity: zod.number(),
});

const itemsStepSchema = zod.object({
  nonNullableObject: zod.object({}),
  items: wrapToArray(singleItemSchema), // swapping this line with the one below results in correct behaviour (2 issues instead of 1)
  // items: singleItemSchema,       
});

const res = itemsStepSchema.safeParse({
  nonNullableObject: null,
  items: {},
});

//@ts-ignore
console.log(res.error.issues.length);  // 3.21 shows 2 errors, 3.22 - only 1
console.log(res);

An error in nonNullableObject hides the preprocess errors. Also, if I remove the preprocess call completely the behaviour becomes correct. This doesn't look right as preprocess stops errors from being populated. 3.21.4: Screenshot 2023-08-21 at 19 06 17

3.22.2: Screenshot 2023-08-21 at 19 05 12

azhiv avatar Aug 22 '23 12:08 azhiv

I just stumbled over this one as well. A simple test case to reproduce:

const result = z
  .array(z.preprocess((v) => v, z.number()))
  .safeParse(['a', 'b'])

expect((result as z.SafeParseError<unknown>).error.errors).toStrictEqual([
  {
    code: 'invalid_type',
    expected: 'number',
    received: 'string',
    path: [0],
    message: 'Expected number, received string',
  },
  {
    code: 'invalid_type',
    expected: 'number',
    received: 'string',
    path: [1],
    message: 'Expected number, received string',
  },
])

This test fails because safeParse only returns the first error. Removing the z.preprocess (which shouldn't do anything in this case anyway) makes the test pass.

I can also confirm that the latest version where this worked as expected was v3.21.4 and only versions v3.22.0 and later are affected.

cmd-johnson avatar Aug 23 '23 08:08 cmd-johnson

fix with https://github.com/colinhacks/zod/pull/2719 ?

maorun avatar Sep 22 '23 14:09 maorun

This seems to be a bug with preprocess. Perhaps using transform and pipe will work for you instead?

const wrapToArray = <T extends z.ZodTypeAny> ( schema: T ) =>
    z.any().transform( x => [ x ] ).pipe( schema.array() )

const schema = z.object( {
    foo: z.number(),
    bar: wrapToArray( z.object( { qux: z.number() } ) ),
} )

const result = schema.safeParse( { foo: 'invalid', bar: 'invalid' } )
!result.success && console.log( result.error.issues )
// [
//     {
//         code: "invalid_type",
//         expected: "number",
//         received: "string",
//         path: [ "foo" ],
//         message: "Expected number, received string"
//     }, {
//         code: "invalid_type",
//         expected: "object",
//         received: "string",
//         path: [ "bar", 0 ],
//         message: "Expected object, received string"
//     }
// ]

If you found my answer satisfactory, please consider supporting me. Even a small amount is greatly appreciated. Thanks friend! 🙏 https://github.com/sponsors/JacobWeisenburger

JacobWeisenburger avatar Sep 23 '23 20:09 JacobWeisenburger

Hey @JacobWeisenburger, your solution works for small projects, but we currently have multiple projects/services with lots of schemas using preprocess and changing them all is not a good option.

Would the fix proposed in https://github.com/colinhacks/zod/pull/2719 be good enough to merge and release a quick fix? Multiple people are trying to update to latest version of Zod to fix the Email Regex ReDoS vulnerability but this preprocess bug introduced in https://github.com/colinhacks/zod/releases/tag/v3.22.0 is a breaking change...

thiagomeireless avatar Oct 04 '23 19:10 thiagomeireless

#2719 seems good enough.

kocyigityunus avatar Oct 30 '23 06:10 kocyigityunus

Seems to be fixed in 3.23.0 with https://github.com/colinhacks/zod/pull/2912

andreyfel avatar Apr 22 '24 10:04 andreyfel