zod icon indicating copy to clipboard operation
zod copied to clipboard

Preprocessing hides errors in zod 3.22.x

Open flavianh opened this issue 1 year ago • 2 comments

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.

flavianh avatar Aug 28 '23 06:08 flavianh

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"]
        }
      ]
    }
  }
]

neummichel avatar Aug 28 '23 14:08 neummichel

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 😅

klaemo avatar Apr 23 '24 10:04 klaemo