zod
                                
                                
                                
                                    zod copied to clipboard
                            
                            
                            
                        wrap internal errors from `preprocess` and `refine`
As explained here zod intentionally does not catch errors from within preprocess and refine.
This is behaving as intended currently. An error in a refinement is considered an application-level bug, not a validation error. The thinking is that catching this error in Zod would make it harder to debug actual real-life bugs, and you can always use try/catch. But if Zod catches these errors, there's no way to opt-out of this and allow errors to bubble up.
However, consider this schema:
z.object({ a: z.array(z.object({ b: z.preprocess(data => { throw new Error('asdf') }, z.date()) }))}).parse({a: [{}]})
The error will be:
Uncaught Error: asdf
    at Object.transform (REPL12:1:66)
    at ZodEffects._parse (/node_modules/zod/lib/types.js:2137:38)
    at ZodObject._parse (/node_modules/zod/lib/types.js:1070:37)
    at ZodObject._parseSync (/node_modules/zod/lib/types.js:109:29)
This makes it difficult to know where the error came from. Consider an object schema with many properties (that may also be nested) - if I get this error I don't know which property caused it.
In contrast, the regular zod parsing errors are amazing:
z.object({ a: z.array(z.object({ b: z.date()})) }).parse({a: [{}]})
Error:
[
  {
    "code": "invalid_type",
    "expected": "date",
    "received": "undefined",
    "path": [
      "a",
      0,
      "b"
    ],
    "message": "Required"
  }
]
It tells me exactly the path to the problem in my object.
Proposed solution
I propose that zod will catch my internal error and wrap it in a zod error (and re-throw it) that will at least tell me where the error occurred in the object.
For example something like:
z.object({ a: z.array(z.object({ b: z.preprocess(data => { throw new Error('asdf') }, z.date()) }))}).parse({a: [{}]})
Expected error:
[
  {
    "code": "preprocess_error",
    "path": [
      "a",
      0,
      "b"
    ],
    "message": "Uncaught Error: asdf",
    "originalError": <the original error object>
  }
]
                                    
                                    
                                    
                                
But allow the behaviour of the "rethrow" to be customizable. I.e. personally I wouldn't want to use the ZodError class at all, and to use my own errors.
I would like to suggest a above PR (https://github.com/colinhacks/zod/pull/3314) as a possible solution for this issue. We could create a similar implementation for refinement as well.