colinhacks / zod

TypeScript-first schema validation with static type inference
https://zod.dev
MIT License
33.09k stars 1.15k forks source link

Bug: .safeParse() should not throw #3756

Open kirkwaiblinger opened 4 days ago

kirkwaiblinger commented 4 days ago

From the docs,

If you don't want Zod to throw errors when validation fails, use .safeParse. This method returns an object containing either the successfully parsed data or a ZodError instance containing detailed information about the validation problems.

This works fine and well so long as you stick to built-in refinements and schemas. But, if you make your own transformations/refinements, and they throw, then safeparse will throw.

const throwingSchema = z.any().transform((args, context) => {
   throw new Error('lol');
});

const resultShouldntThrow = throwingSchema.safeParse("please don't throw!");

I understand that user-defined .transform() and .refine() functions are not intended to throw, so they constitute an invalid schema. However, I would expect that to be reported from .safeParse() in a non-throwing way, for example

throwingSchema.safeParse("please don't throw!"); // { valid: false, code: 'INVALID_SCHEMA', message: 'Exception was thrown during user-defined `.transform()` callback' }

If we really want a method called "safeX" to throw, I would expect it to come in the form of an opt-in option, such as

throwingSchema.safeParse("please don't throw!", { throwOnInvalidSchema: true }); // Uncaught: ZodErrorInvalidSchema

Otherwise, we're stuck with

let parseResult;
try {
   parseResult = throwingSchema.safeParse("please don't throw!");
} catch (error) {
   // handle secondary error path
}
if (!parseResult.valid) {
   // handle primary error path
}

which defeats the purpose of safeParse in the first place.