zod icon indicating copy to clipboard operation
zod copied to clipboard

Error when using `coerce` function V4

Open nguyendhuy1997 opened this issue 5 months ago • 1 comments

Hi,

Here is my schema before migrate to V4

const schema = z.string().date({ message: 'Invalid ISO 8601 date-time format. Expected formats: YYYY-MM-DDTHH:MM:SS.sssZ' }) .pipe(z.coerce.date())

after migrate to V4

const schema = z.iso.datetime({ error: 'Invalid ISO 8601 date-time format. Expected formats: YYYY-MM-DDTHH:MM:SS.sssZ', }) .pipe(z.coerce.date())

schema.safeParse('invalid data')

Then it will return errors

error: ZodError: [ { "origin": "string", "code": "invalid_format", "format": "datetime", "pattern": "/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/", "path": [], "message": "Invalid ISO 8601 date-time format. Expected formats: YYYY-MM-DDTHH:MM:SS.sssZ" }, { "expected": "date", "code": "invalid_type", "received": "Invalid Date", "path": [], "message": "Invalid input: expected date, received Date" } ]

Before migrating to v4, it only returned the first issue instead of both. By using .transform((value) => new Date(value) it still work as normal. Is this expected?

nguyendhuy1997 avatar Jun 23 '25 09:06 nguyendhuy1997

Hi @nguyendhuy1997! I'm Dosu and I’m helping the zod team.

This is expected in Zod v4. The .pipe() method now collects errors from both the input and output schemas, so when you use something like z.iso.datetime().pipe(z.coerce.date()), you'll get errors for both the format (from the ISO datetime check) and the type (from the date coercion) if the input fails both stages. This is an intentional design change to provide more comprehensive error reporting and make debugging easier [docs].

If you want to replicate the old behavior (only the first error), you can use .transform() or .preprocess() instead, which will typically stop at the first failure. For example:

const schema = z.iso.datetime().transform((value) => new Date(value));

Or, using z.preprocess:

const schema = z.preprocess(
  (val) => typeof val === "string" ? new Date(val) : val,
  z.date()
);

For more details on the new error handling and .pipe() behavior, see the v4 changelog and API docs.

Let me know if this answers your question or if you need more help! If you're satisfied, feel free to close the issue.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

dosubot[bot] avatar Jun 23 '25 09:06 dosubot[bot]

In Zod 4, only invalid_type issues are considered "non-continuable". For other issue types (including the invalid_format issue produced by z.iso.datetime()) validation will continue (including across pipes) by default. You can change this with the abort flag:

const schema = z.iso
  .datetime({ error: "Invalid ISO 8601 date-time format. Expected formats: YYYY-MM-DDTHH:MM:SS.sssZ", abort: true })
  .pipe(z.coerce.date());

console.log(schema.safeParse("invalid data")); // only one issue

colinhacks avatar Jul 03 '25 22:07 colinhacks