Standardize error handling so failures remain diagnosable and user-facing messages are complete.

Apply these rules: 1) Preserve diagnostic context when propagating/rethrowing

2) Ensure every issue code is explicitly mapped

3) Extract/format messages safely (no unsafe casts)

4) Don’t unintentionally suppress other validations

Example: safe message extraction

try {
  return JSON.parse(val);
} catch (e: unknown) {
  const message = e instanceof Error ? e.message : String(e);
  ctx.addIssue({
    code: ZodIssueCode.invalid_string,
    validation: "json",
    message,
  });
}

Example: stack-preserving handler shape

const zodErrorHandler = (messageOrObject: string | unknown) => {
  if (typeof messageOrObject === "string") {
    throw ZodError.fromString(messageOrObject);
  }
  throw messageOrObject; // keep original throw semantics where possible
};

Checklist for PRs in this area