handling-validation-errors

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Handling Validation Errors in z-schema

z-schema 验证错误处理

z-schema reports validation errors as
ValidateError
objects containing a
.details
array of
SchemaErrorDetail
. This skill covers inspecting, filtering, mapping, and presenting these errors.
z-schema 将验证错误以
ValidateError
对象的形式返回,该对象包含
SchemaErrorDetail
类型的
.details
数组。本教程涵盖这些错误的检查、过滤、映射和展示方法。

Error structure

错误结构

typescript
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';
ValidateError
extends
Error
:
PropertyTypeDescription
.name
string
Always
'z-schema validation error'
.message
string
Summary message
.details
SchemaErrorDetail[]
All individual errors
Each
SchemaErrorDetail
:
FieldTypeDescription
message
string
Human-readable text, e.g.
"Expected type string but found type number"
code
string
Machine-readable code, e.g.
"INVALID_TYPE"
params
(string | number | Array)[]
Values filling the message template placeholders
path
string | Array<string | number>
JSON Pointer to the failing value (
"#/age"
or
["age"]
)
keyword
string?
Schema keyword that caused the error (
"type"
,
"required"
, etc.)
inner
SchemaErrorDetail[]?
Sub-errors from combinators (
anyOf
,
oneOf
,
not
)
schemaPath
Array<string | number>?
Path within the schema to the constraint
schemaId
string?
Schema ID if present
title
string?
Schema
title
if present
description
string?
Schema
description
if present
typescript
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';
ValidateError
继承自
Error
属性类型描述
.name
string
固定为
'z-schema validation error'
.message
string
摘要消息
.details
SchemaErrorDetail[]
所有单个错误列表
每个
SchemaErrorDetail
包含的字段:
字段类型描述
message
string
人类可读的提示文本,例如
"Expected type string but found type number"
code
string
机器可读的错误码,例如
"INVALID_TYPE"
params
(string | number | Array)[]
填充消息模板占位符的具体值
path
string | Array<string | number>
指向出错值的JSON Pointer(
"#/age"
["age"]
keyword
string?
触发错误的Schema关键字(
"type"
"required"
等)
inner
SchemaErrorDetail[]?
来自组合器(
anyOf
oneOf
not
)的子错误
schemaPath
Array<string | number>?
约束条件在Schema内部的路径
schemaId
string?
Schema ID(如果存在)
title
string?
Schema的
title
字段(如果存在)
description
string?
Schema的
description
字段(如果存在)

Capturing errors

捕获错误

Try/catch (default mode)

Try/catch(默认模式)

typescript
import ZSchema from 'z-schema';

const validator = ZSchema.create();

try {
  validator.validate(data, schema);
} catch (err) {
  if (err instanceof ValidateError) {
    for (const detail of err.details) {
      console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
    }
  }
}
typescript
import ZSchema from 'z-schema';

const validator = ZSchema.create();

try {
  validator.validate(data, schema);
} catch (err) {
  if (err instanceof ValidateError) {
    for (const detail of err.details) {
      console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
    }
  }
}

Safe mode (no try/catch)

安全模式(无需try/catch)

typescript
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(data, schema);

if (!valid && err) {
  for (const detail of err.details) {
    console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
  }
}
typescript
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(data, schema);

if (!valid && err) {
  for (const detail of err.details) {
    console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
  }
}

Walking nested errors

遍历嵌套错误

Combinators (
anyOf
,
oneOf
,
not
) produce nested
inner
errors. A recursive walker handles any depth:
typescript
function walkErrors(details: SchemaErrorDetail[], depth = 0): void {
  for (const detail of details) {
    const indent = '  '.repeat(depth);
    console.log(`${indent}[${detail.code}] ${detail.path}: ${detail.message}`);
    if (detail.inner) {
      walkErrors(detail.inner, depth + 1);
    }
  }
}

const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  walkErrors(err.details);
}
组合器(
anyOf
oneOf
not
)会生成嵌套的
inner
错误,递归遍历函数可以处理任意深度的嵌套:
typescript
function walkErrors(details: SchemaErrorDetail[], depth = 0): void {
  for (const detail of details) {
    const indent = '  '.repeat(depth);
    console.log(`${indent}[${detail.code}] ${detail.path}: ${detail.message}`);
    if (detail.inner) {
      walkErrors(detail.inner, depth + 1);
    }
  }
}

const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  walkErrors(err.details);
}

Collecting all leaf errors

收集所有叶子错误

Flatten the tree to get every concrete error, skipping combinator wrappers:
typescript
function collectLeafErrors(details: SchemaErrorDetail[]): SchemaErrorDetail[] {
  const leaves: SchemaErrorDetail[] = [];
  for (const detail of details) {
    if (detail.inner && detail.inner.length > 0) {
      leaves.push(...collectLeafErrors(detail.inner));
    } else {
      leaves.push(detail);
    }
  }
  return leaves;
}
将错误树扁平化,获取所有具体错误,跳过组合器包装层:
typescript
function collectLeafErrors(details: SchemaErrorDetail[]): SchemaErrorDetail[] {
  const leaves: SchemaErrorDetail[] = [];
  for (const detail of details) {
    if (detail.inner && detail.inner.length > 0) {
      leaves.push(...collectLeafErrors(detail.inner));
    } else {
      leaves.push(detail);
    }
  }
  return leaves;
}

Mapping errors to form fields

将错误映射到表单字段

Convert JSON Pointer paths to field names for UI form validation:
typescript
function pathToFieldName(path: string | Array<string | number>): string {
  if (Array.isArray(path)) {
    return path.join('.');
  }
  // JSON Pointer string: "#/address/city" → "address.city"
  return path.replace(/^#\/?/, '').replace(/\//g, '.');
}

function errorsToFieldMap(details: SchemaErrorDetail[]): Record<string, string[]> {
  const map: Record<string, string[]> = {};
  const leaves = collectLeafErrors(details);
  for (const detail of leaves) {
    const field = pathToFieldName(detail.path) || '_root';
    (map[field] ??= []).push(detail.message);
  }
  return map;
}

// Usage
const { valid, err } = validator.validateSafe(formData, schema);
if (!valid && err) {
  const fieldErrors = errorsToFieldMap(err.details);
  // { "email": ["Expected type string but found type number"],
  //   "age": ["Value 150 is greater than maximum 120"] }
}
将JSON Pointer路径转换为字段名,用于UI表单验证:
typescript
function pathToFieldName(path: string | Array<string | number>): string {
  if (Array.isArray(path)) {
    return path.join('.');
  }
  // JSON Pointer字符串转换:"#/address/city" → "address.city"
  return path.replace(/^#\/?/, '').replace(/\//g, '.');
}

function errorsToFieldMap(details: SchemaErrorDetail[]): Record<string, string[]> {
  const map: Record<string, string[]> = {};
  const leaves = collectLeafErrors(details);
  for (const detail of leaves) {
    const field = pathToFieldName(detail.path) || '_root';
    (map[field] ??= []).push(detail.message);
  }
  return map;
}

// 使用示例
const { valid, err } = validator.validateSafe(formData, schema);
if (!valid && err) {
  const fieldErrors = errorsToFieldMap(err.details);
  // { "email": ["Expected type string but found type number"],
  //   "age": ["Value 150 is greater than maximum 120"] }
}

Using array paths

使用数组格式的路径

Enable
reportPathAsArray
for easier programmatic access:
typescript
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] instead of "#/address/city"
开启
reportPathAsArray
配置,方便编程访问:
typescript
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] 而非 "#/address/city"

Filtering errors

过滤错误

Per-call filtering

单次调用过滤

Pass
includeErrors
or
excludeErrors
as the third argument:
typescript
// Only report type mismatches
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });

// Suppress string-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });
调用validate时传入第三个参数
includeErrors
excludeErrors
typescript
// 仅上报类型不匹配错误
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });

// 屏蔽字符串长度错误
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });

Programmatic post-filtering

编程后置过滤

typescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  const typeErrors = err.details.filter((d) => d.code === 'INVALID_TYPE');
  const requiredErrors = err.details.filter((d) => d.code === 'OBJECT_MISSING_REQUIRED_PROPERTY');
}
typescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  const typeErrors = err.details.filter((d) => d.code === 'INVALID_TYPE');
  const requiredErrors = err.details.filter((d) => d.code === 'OBJECT_MISSING_REQUIRED_PROPERTY');
}

Custom error messages

自定义错误消息

Map error codes to user-friendly messages:
typescript
const friendlyMessages: Record<string, (detail: SchemaErrorDetail) => string> = {
  INVALID_TYPE: (d) => `${pathToFieldName(d.path)} must be a ${d.params[0]}`,
  OBJECT_MISSING_REQUIRED_PROPERTY: (d) => `${d.params[0]} is required`,
  MINIMUM: (d) => `${pathToFieldName(d.path)} must be at least ${d.params[1]}`,
  MAXIMUM: (d) => `${pathToFieldName(d.path)} must be at most ${d.params[1]}`,
  MIN_LENGTH: (d) => `${pathToFieldName(d.path)} must be at least ${d.params[1]} characters`,
  MAX_LENGTH: (d) => `${pathToFieldName(d.path)} must be at most ${d.params[1]} characters`,
  PATTERN: (d) => `${pathToFieldName(d.path)} has an invalid format`,
  ENUM_MISMATCH: (d) => `${pathToFieldName(d.path)} must be one of the allowed values`,
  INVALID_FORMAT: (d) => `${pathToFieldName(d.path)} is not a valid ${d.params[0]}`,
};

function toFriendlyMessage(detail: SchemaErrorDetail): string {
  const fn = friendlyMessages[detail.code];
  return fn ? fn(detail) : detail.message;
}
将错误码映射为用户友好的提示消息:
typescript
const friendlyMessages: Record<string, (detail: SchemaErrorDetail) => string> = {
  INVALID_TYPE: (d) => `${pathToFieldName(d.path)} 必须是 ${d.params[0]} 类型`,
  OBJECT_MISSING_REQUIRED_PROPERTY: (d) => `${d.params[0]} 为必填项`,
  MINIMUM: (d) => `${pathToFieldName(d.path)} 最小为 ${d.params[1]}`,
  MAXIMUM: (d) => `${pathToFieldName(d.path)} 最大为 ${d.params[1]}`,
  MIN_LENGTH: (d) => `${pathToFieldName(d.path)} 长度至少为 ${d.params[1]} 个字符`,
  MAX_LENGTH: (d) => `${pathToFieldName(d.path)} 长度最多为 ${d.params[1]} 个字符`,
  PATTERN: (d) => `${pathToFieldName(d.path)} 格式非法`,
  ENUM_MISMATCH: (d) => `${pathToFieldName(d.path)} 必须是允许的取值之一`,
  INVALID_FORMAT: (d) => `${pathToFieldName(d.path)} 不是合法的 ${d.params[0]} 格式`,
};

function toFriendlyMessage(detail: SchemaErrorDetail): string {
  const fn = friendlyMessages[detail.code];
  return fn ? fn(detail) : detail.message;
}

Stopping at first error

遇到第一个错误即停止

For fail-fast scenarios:
typescript
const validator = ZSchema.create({ breakOnFirstError: true });
This reports only the first error encountered, reducing noise during iterative fixing.
适用于快速失败的场景:
typescript
const validator = ZSchema.create({ breakOnFirstError: true });
这种配置只会返回遇到的第一个错误,减少迭代修复过程中的干扰信息。

Using the keyword field

使用keyword字段

The
keyword
field tells you which schema keyword triggered the error — useful for categorizing errors programmatically:
typescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  for (const detail of err.details) {
    switch (detail.keyword) {
      case 'required':
        // handle missing field
        break;
      case 'type':
        // handle type mismatch
        break;
      case 'format':
        // handle format failure
        break;
    }
  }
}
keyword
字段会告诉你是哪个Schema关键字触发了错误,适用于编程对错误进行分类:
typescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  for (const detail of err.details) {
    switch (detail.keyword) {
      case 'required':
        // 处理缺失字段的逻辑
        break;
      case 'type':
        // 处理类型不匹配的逻辑
        break;
      case 'format':
        // 处理格式校验失败的逻辑
        break;
    }
  }
}

Reference files

参考文件

For the full error code list with descriptions, see the validating-json-data skill's error-codes reference.
完整的错误码列表及说明,请查看JSON数据验证技能的错误码参考