Documentation Index
Fetch the complete documentation index at: https://mintlify.com/colinhacks/zod/llms.txt
Use this file to discover all available pages before exploring further.
Error Maps
Error maps allow you to customize error messages based on the validation issue type and context.
Basic Error Map
An error map is a function that receives an issue and returns a custom message:
import * as z from 'zod';
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "invalid_type") {
if (issue.expected === "string") {
return { message: "bad type!" };
}
}
if (issue.code === "custom") {
return { message: `less-than-${issue.params?.minimum}` };
}
return undefined; // Use default message
};
const result = z.string().safeParse(234, { error: errorMap });
// Error message: "bad type!"
Error Map Return Values
Error maps can return:
{ message: string } - Custom error message
string - Shorthand for { message: string }
undefined or null - Fall back to default message
const errorMap: z.ZodErrorMap = (issue) => {
// Object form
if (issue.code === "invalid_type") {
return { message: "Invalid type" };
}
// String shorthand
if (issue.code === "too_small") {
return "Too small";
}
// Fall back to default
return undefined;
};
Contextual Error Maps
Apply error maps to specific parse operations:
const schema = z.string();
// Use custom error map for this parse only
const result = schema.safeParse(123, {
error: (issue) => ({ message: "contextual" })
});
Refinements with Error Maps
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "custom") {
return { message: `less-than-${issue.params?.minimum}` };
}
return undefined;
};
const schema = z.number().refine((val) => val >= 3, {
params: { minimum: 3 }
});
const result = schema.safeParse(2, { error: errorMap });
// {
// "code": "custom",
// "path": [],
// "params": { "minimum": 3 },
// "message": "less-than-3"
// }
Schema-Bound Error Maps
Bind error maps directly to schemas:
const stringWithCustomError = z.string({
error: () => "bound"
});
const result = stringWithCustomError.safeParse(1234);
// message: "bound"
Bound vs Contextual Precedence
Schema-bound error maps take precedence over contextual ones:
const boundSchema = z.string({
error: () => "bound"
});
// Contextual error map is ignored
const result = boundSchema.safeParse(undefined, {
error: () => ({ message: "contextual" })
});
// message: "bound" (not "contextual")
Global Error Configuration
Set a global custom error map using z.config():
// Set global error map
z.config({
customError: () => ({ message: "override" })
});
const schema = z.string().min(10);
const result = schema.safeParse("tooshort");
// All errors use the global error map
// Clear global error map
z.config({ customError: undefined });
Global error maps affect all validations in your application. Use with caution and consider schema-bound or contextual error maps for more targeted customization.
Error Map Issue Types
Invalid Type Issues
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "invalid_type") {
const expected = issue.expected; // "string" | "number" | "boolean" | ...
const received = issue.input; // The actual input value
return { message: `Expected ${expected}, got ${typeof received}` };
}
return undefined;
};
Size Constraint Issues
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "too_small") {
const { origin, minimum, inclusive, exact } = issue;
if (origin === "string") {
return { message: `String must be at least ${minimum} characters` };
}
if (origin === "array") {
return { message: `Array must contain at least ${minimum} items` };
}
}
if (issue.code === "too_big") {
const { origin, maximum } = issue;
return { message: `${origin} exceeds maximum of ${maximum}` };
}
return undefined;
};
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "invalid_format") {
const format = issue.format; // "email" | "url" | "uuid" | "regex" | ...
if (format === "email") {
return { message: "Please enter a valid email address" };
}
if (format === "regex" && issue.pattern) {
return { message: `Must match pattern: ${issue.pattern}` };
}
}
return undefined;
};
Custom Issues with Params
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "custom" && issue.params) {
// Access custom params from refinements
if ('minimum' in issue.params) {
return { message: `Value must be at least ${issue.params.minimum}` };
}
if ('field' in issue.params) {
return { message: `Invalid ${issue.params.field}` };
}
}
return undefined;
};
Advanced Error Map Patterns
Path-Aware Error Messages
const errorMap: z.ZodErrorMap = (issue) => {
const path = issue.path ?? [];
if (path.length > 0) {
const fieldName = path[path.length - 1];
return { message: `Invalid ${fieldName}: ${issue.code}` };
}
return undefined;
};
const schema = z.object({
items: z.array(z.string()).refine(
(data) => data.length > 3,
{ path: ["items-too-few"] }
)
});
const result = schema.safeParse({ items: ["first"] }, { error: errorMap });
// Path will be ["items", "items-too-few"]
Unrecognized Keys
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "unrecognized_keys") {
const keys = issue.keys; // Array of unrecognized key names
return { message: `Unknown fields: ${keys.join(", ")}` };
}
return undefined;
};
Invalid Union
const errorMap: z.ZodErrorMap = (issue) => {
if (issue.code === "invalid_union") {
if (issue.errors.length > 0) {
// No match found
return { message: "Value doesn't match any of the expected types" };
} else if (issue.inclusive === false) {
// Multiple matches (for non-inclusive unions)
return { message: "Value matches multiple types" };
}
}
return undefined;
};
Combining Error Messages
Hard-Coded Message with Error Map
const schema = z.string().refine(
(val) => val.length > 12,
{
params: { minimum: 13 },
message: "override" // This takes precedence
}
);
const result = schema.safeParse("asdf", {
error: () => "contextual"
});
// message: "override" (hard-coded message wins)
Error and Message Conflict
// This will throw an error - cannot use both
try {
z.string().refine((_) => true, {
message: "override",
error: (iss) => iss.input === undefined ? "asdf" : null
});
} catch (e) {
// Error: Cannot use both 'message' and 'error' options
}
You cannot use both message and error options together. The message option is a shorthand that takes precedence.
Empty String Messages
You can explicitly set empty error messages:
const schema = z.string().max(1, { message: "" });
const result = schema.safeParse("asdf");
// message: ""
Best Practices
- Return undefined for defaults - Let Zod generate standard messages when appropriate
- Use params for context - Pass metadata through
params for dynamic messages
- Prefer schema-bound for reusable schemas - Bind error maps to schemas you’ll reuse
- Use contextual for one-off customization - Apply custom error maps at parse time for specific cases
- Avoid global error maps in libraries - They affect all Zod usage in the application
- Consider internationalization - Error maps are perfect for translating messages