Zod 4 not executing check
and refine
after omit
and pick
#4706
Replies: 4 comments 1 reply
-
Hi @rhinodavid! I'm Dosu and I’m helping the zod team. This is expected behavior in Zod v4: when you use If you need to apply custom validation to a picked/omitted schema, you should re-apply your Let me know if you need a code example or want to discuss possible workarounds for your use case. If this answers your question, please feel free to close the issue! To reply, just mention @dosu. How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other |
Beta Was this translation helpful? Give feedback.
-
Well kudos to you @dosu. I'm sure there are wider reasons for this decision but man it is scary. I feel like I need a lint rule to disable |
Beta Was this translation helpful? Give feedback.
-
I had closed this after the robot pointed me to #4427 but the discussion there is adjacent but not the same, as that one occurs at the type level. Suppose we've created an entity "record" with a check: const zRecord = z.object({
id: z.uuid(),
property: z.string()
}).refine(({ property }) => property === "something"); Here's what I'm afraid of. Someone (me) will create a tRPC endpoint where a "record" gets made. The database will add the ID, so the input to the endpoint will have the ID omitted: const router = t.router({
createRecord: t.procedure
.input(zRecord.omit({ id: true }))
.output(zRecord)
.mutation(({ input }) => { return insertRecord(record); })
}); We just disabled the check and the only way to know it is to have a deep understanding of the framework. There are certainly workarounds (don't put the id on Otherwise congrats on v4 -- it looks awesome and I'll be excited to upgrade once I can figure out a way to prevent this from happening. I'm especially excited about the built-in JSON schema support. |
Beta Was this translation helpful? Give feedback.
-
@rhinodavid where did you land on this? We just upgraded to v4 and are hitting the same issue: const EventSchema = z.object({
startAt: DateSchema,
endAt: DateSchema,
// ... otherFields ...
})
.refine(
({ startAt, endAt }) => endAt >= startAt,
{
error: 'Event cannot end before it starts',
path: ['endAt'],
when: ({ value }) => EventSchema.pick({ startAt: true, endAt: true }).safeParse(value).success,
},
);
// this will not run the refinement, and there is no lint failure or other indication warning devs
const UhOhSchema = EventSchema.omit({ title: true }); It almost makes us want to go back to zod3, where you were forced to re-run refinements because they couldn't be extended in any way |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Consider this schema with a
check
that fails if the value ofhello
is notworld
.When I parse this it behaves as expected:
However, if I omit the
number
key the check doesn't execute:I've confirmed that this behaves the same with
refine
. It also behaves the same if one usespick
instead ofomit
.In Zod v3 trying to
pick
/omit
after arefine
raises a type error:I'm unsure what the intended behavior here is supposed to be. It seems like the options are:
refine
/check
with values that might now beundefined
. This seems contrary to the sprit of Zod since the types on thecheck
are no longer correct.I poked around on the issues and discussion and didn't see this issue noted anywhere. Hopefully I'm not duplicating a known issue here. Thanks in advance to whomever looks into this.
Beta Was this translation helpful? Give feedback.
All reactions