Prompt
Standardize error handling so failures remain diagnosable and user-facing messages are complete.
Apply these rules: 1) Preserve diagnostic context when propagating/rethrowing
- If you rebuild or re-aggregate errors, do not unintentionally lose stack trace/throw location. Centralize error creation/throwing so the actual
throwhappens in a predictable place, and avoid patterns that always create a fresh error object without considering stack semantics.
2) Ensure every issue code is explicitly mapped
- When adding a new
ZodIssueCode(or changing which codes are emitted), updatedefaultErrorMapand/or locale maps so users don’t see generic fallback messages.
3) Extract/format messages safely (no unsafe casts)
- Don’t assume
catch (error)is anError. Preferinstanceof Erroror a shared helper that guarantees a string.
4) Don’t unintentionally suppress other validations
- If you use early returns in validation logic, verify you’re not preventing other expected errors from being added for the same input.
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
- New/changed
ZodIssueCodehas explicit message mapping (default + locales if needed). - Validation control flow (early returns) can’t hide required additional issues.
- No
error as Errorwithoutinstanceofor a safe conversion helper. - Error propagation/rewrap logic reviewed for stack/throw-location implications.