💎 Zod 4 现已稳定发布! 阅读公告。
Zod logo

定制错误

Edit this page

在 Zod 中,验证错误以 z.core.$ZodError 类的实例形式呈现。

zod 包中的 ZodError 类是一个子类,实现了一些额外的便利方法。

$ZodError 的实例包含 .issues 数组。每个 issue 包含一个人类可读的 message 和关于该问题的其他结构化元数据。

import * as z from "zod";
 
const result = z.string().safeParse(12); // { success: false, error: ZodError }
result.error.issues;
// [
//   {
//     expected: 'string',
//     code: 'invalid_type',
//     path: [],
//     message: '无效输入:预期字符串,收到数字'
//   }
// ]

每个 issue 都包含一个带有人类可读错误信息的 message 属性。错误信息可以通过多种方式自定义。

error 参数

几乎所有 Zod API 都接受一个可选的错误消息。

z.string("不是字符串!");

这个自定义错误会作为任何来自该模式的验证问题的 message 属性显示。

z.string("不是字符串!").parse(12);
// ❌ 抛出 ZodError {
//   issues: [
//     {
//       expected: 'string',
//       code: 'invalid_type',
//       path: [],
//       message: '不是字符串!'   <-- 👀 自定义错误信息
//     }
//   ]
// }

所有 z 函数和模式方法均接受自定义错误。

z.string("错误!");
z.string().min(5, "太短了!");
z.uuid("错误的 UUID!");
z.iso.date("无效日期!");
z.array(z.string(), "不是数组!");
z.array(z.string()).min(5, "元素太少!");
z.set(z.string(), "无效集合!");

如果愿意,也可以传入一个包含 error 参数的对象。

z.string({ error: "错误!" });
z.string().min(5, { error: "太短了!" });
z.uuid({ error: "错误的 UUID!" });
z.iso.date({ error: "无效日期!" });
z.array(z.string(), { error: "错误的数组!" });
z.array(z.string()).min(5, { error: "元素太少!" });
z.set(z.string(), { error: "无效集合!" });

error 参数也可以接受一个函数。错误定制函数在 Zod 中称为 错误映射(error map)。如果发生验证错误,错误映射会在解析时运行。

z.string({ error: () => `[${Date.now()}]: 验证失败。` });

注意 — 在 Zod v3 中,message(字符串)和 errorMap(函数)是分开的参数。它们在 Zod 4 中统一为 error

错误映射函数接收一个上下文对象,你可以根据验证问题自定义错误信息。

z.string({
  error: (iss) => iss.input === undefined ? "该字段为必填项。" : "无效输入。"
});

对于高级场景,iss 对象提供了更多信息供你定制错误。

z.string({
  error: (iss) => {
    iss.code; // 问题代码
    iss.input; // 输入数据
    iss.inst; // 产生该问题的模式/检查
    iss.path; // 错误路径
  },
});

根据你使用的 API,可能会有其他可用属性。可以使用 TypeScript 的自动补全探索这些属性。

z.string().min(5, {
  error: (iss) => {
    // ...同上
    iss.minimum; // 最小值
    iss.inclusive; // 是否包含最小值
    return `密码必须有至少 ${iss.minimum} 个字符`;
  },
});

返回 undefined 以避免自定义错误消息,并回退到默认消息。(更具体地说,Zod 将控制权交给 优先级链 中的下一个错误映射。)这对于选择性地自定义某些错误消息而不自定义其他消息非常有用。

z.int64({
  error: (issue) => {
    // 覆盖 too_big 错误消息
    if (issue.code === "too_big") {
      return { message: `值必须小于 <${issue.maximum}` };
    }
 
    // 使用默认错误
    return undefined;
  },
});

针对单次解析的错误定制

要针对每次解析定制错误,可以向解析方法 parse 传入错误映射:

const schema = z.string();
 
schema.parse(12, {
  error: iss => "单次解析的自定义错误"
});

这比模式级别的自定义消息优先级

const schema = z.string({ error: "最高优先级" });
const result = schema.safeParse(12, {
  error: (iss) => "较低优先级",
});
 
result.error.issues;
// [{ message: "最高优先级", ... }]

iss 对象是所有可能的 issue 类型的区分联合类型。使用 code 属性区分它们。

详见所有 Zod issue 代码的拆解,参考 zod/v4/core 文档。

const result = schema.safeParse(12, {
  error: (iss) => {
    if (iss.code === "invalid_type") {
      return `无效类型,期望 ${iss.expected}`;
    }
    if (iss.code === "too_small") {
      return `最小值为 ${iss.minimum}`;
    }
    // ...
  }
});

在 issue 中包含输入值

默认情况下,Zod 不会在 issues 中包含输入数据。这是为了防止无意中记录可能敏感的输入数据。要在每个 issue 中包含输入数据,请使用 reportInput 标志:

z.string().parse(12, {
  reportInput: true
})
 
// ZodError: [
//   {
//     "expected": "string",
//     "code": "invalid_type",
//     "input": 12, // 👀
//     "path": [],
//     "message": "无效输入:预期字符串,收到数字"
//   }
// ]

全局错误定制

要指定全局错误映射,使用 z.config() 设置 Zod 的 customError 配置选项:

z.config({
  customError: (iss) => {
    return "全局修改后的错误";
  },
});

全局错误消息的优先级低于模式级别或单次解析的错误消息。

iss 对象是所有可能的 issue 类型的区分联合类型。使用 code 属性区分它们。

详见所有 Zod issue 代码的拆解,参考 zod/v4/core 文档。

z.config({
  customError: (iss) => {
    if (iss.code === "invalid_type") {
      return `无效类型,期望 ${iss.expected}`;
    }
    if (iss.code === "too_small") {
      return `最小值为 ${iss.minimum}`;
    }
    // ...
  },
});

国际化

为支持错误信息的国际化,Zod 提供了多个内置语言包(locales)。它们从 zod/v4/core 包导出。

注意 — 常规的 zod 库会自动加载 en 语言环境。Zod Mini 默认不加载任何语言环境;相反,所有错误消息默认显示为 无效输入

import * as z from "zod";
import { en } from "zod/locales"
 
z.config(en());

若想懒加载语言包,可以考虑动态导入:

import * as z from "zod";
 
async function loadLocale(locale: string) {
  const { default: locale } = await import(`zod/v4/locales/${locale}.js`);
  z.config(locale());
};
 
await loadLocale("fr");

为了方便,所有语言包作为 z.locales"zod" 导出。在某些打包器中,这可能无法进行摇树优化。

import * as z from "zod";
 
z.config(z.locales.en());

语言包列表

可用语言包如下:

  • ar — Arabic
  • az — Azerbaijani
  • be — Belarusian
  • bg — Bulgarian
  • ca — Catalan
  • cs — Czech
  • da — Danish
  • de — German
  • en — English
  • eo — Esperanto
  • es — Spanish
  • fa — Farsi
  • fi — Finnish
  • fr — French
  • frCA — Canadian French
  • he — Hebrew
  • hu — Hungarian
  • id — Indonesian
  • is — Icelandic
  • it — Italian
  • ja — Japanese
  • ka — Georgian
  • km — Khmer
  • ko — Korean
  • lt — Lithuanian
  • mk — Macedonian
  • ms — Malay
  • nl — Dutch
  • no — Norwegian
  • ota — Türkî
  • ps — Pashto
  • pl — Polish
  • pt — Portuguese
  • ru — Russian
  • sl — Slovenian
  • sv — Swedish
  • ta — Tamil
  • th — Thai
  • tr — Türkçe
  • uk — Ukrainian
  • ur — Urdu
  • vi — Tiếng Việt
  • zhCN — Simplified Chinese
  • zhTW — Traditional Chinese
  • yo — Yorùbá

错误优先级

下面是确定错误优先级的快速参考:如果定义了多个错误定制,哪个优先?按从高到底优先级排序:

  1. 模式级错误 — 任何“硬编码”到模式定义中的错误消息。
z.string("不是字符串!");
  1. 单次解析错误 — 传入 .parse() 方法的自定义错误映射。
z.string().parse(12, {
  error: (iss) => "我的自定义错误"
});
  1. 全局错误映射 — 传给 z.config() 的自定义错误映射。
z.config({
  customError: (iss) => "我的自定义错误"
});
  1. 语言包错误映射 — 传给 z.config() 的语言包自定义错误映射。
z.config(z.locales.en());

On this page