重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
handling-validation-errors by zaggino/z-schema
npx skills add https://github.com/zaggino/z-schema --skill handling-validation-errorsz-schema 将验证错误报告为包含 .details 数组(由 SchemaErrorDetail 组成)的 ValidateError 对象。本技能涵盖检查、过滤、映射和呈现这些错误。
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';
ValidateError 继承自 Error:
| 属性 | 类型 | 描述 |
|---|---|---|
.name |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
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 |
path | `string | Array<string |
keyword | string? | 导致错误的模式关键字("type"、"required" 等) |
inner | SchemaErrorDetail[]? | 来自组合器(anyOf、oneOf、not)的子错误 |
schemaPath | `Array<string | number>?` |
schemaId | string? | 模式 ID(如果存在) |
title | string? | 模式 title(如果存在) |
description | string? | 模式 description(如果存在) |
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}`);
}
}
}
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}`);
}
}
组合器(anyOf、oneOf、not)会产生嵌套的 inner 错误。递归遍历器可以处理任意深度:
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);
}
展平树以获取每个具体错误,跳过组合器包装器:
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;
}
将 JSON 指针路径转换为字段名称,用于 UI 表单验证:
function pathToFieldName(path: string | Array<string | number>): string {
if (Array.isArray(path)) {
return path.join('.');
}
// JSON 指针字符串:"#/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"] }
}
启用 reportPathAsArray 以便于编程访问:
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] 而不是 "#/address/city"
将 includeErrors 或 excludeErrors 作为第三个参数传递:
// 仅报告类型不匹配
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });
// 抑制字符串长度错误
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });
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');
}
将错误代码映射到用户友好的消息:
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]}`,
ARRAY_UNEVALUATED_ITEMS: () => `数组包含任何模式都未涵盖的项`,
OBJECT_UNEVALUATED_PROPERTIES: (d) => `${d.params[0]} 不被模式允许`,
COLLECT_EVALUATED_DEPTH_EXCEEDED: (d) => `模式嵌套过深(最大 ${d.params[0]})`,
MAX_RECURSION_DEPTH_EXCEEDED: (d) => `递归深度超出(最大 ${d.params[0]})`,
};
function toFriendlyMessage(detail: SchemaErrorDetail): string {
const fn = friendlyMessages[detail.code];
return fn ? fn(detail) : detail.message;
}
对于快速失败场景:
const validator = ZSchema.create({ breakOnFirstError: true });
这仅报告遇到的第一个错误,减少迭代修复期间的干扰。
keyword 字段告诉你是哪个模式关键字触发了错误 —— 对于以编程方式对错误进行分类很有用:
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;
}
}
}
有关完整的错误代码列表及描述,请参阅 validating-json-data 技能的 error-codes 参考。
每周安装次数
50
代码仓库
GitHub 星标数
342
首次出现
2026年2月27日
安全审计
安装于
github-copilot50
mcpjam10
claude-code10
junie10
windsurf10
zencoder10
z-schema reports validation errors as ValidateError objects containing a .details array of SchemaErrorDetail. This skill covers inspecting, filtering, mapping, and presenting these errors.
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';
ValidateError extends Error:
| Property | Type | Description |
|---|---|---|
.name | string | Always 'z-schema validation error' |
.message | string | Summary message |
.details | SchemaErrorDetail[] | All individual errors |
Each SchemaErrorDetail:
| Field | Type | Description |
|---|---|---|
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 |
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}`);
}
}
}
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}`);
}
}
Combinators (anyOf, oneOf, not) produce nested inner errors. A recursive walker handles any depth:
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);
}
Flatten the tree to get every concrete error, skipping combinator wrappers:
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;
}
Convert JSON Pointer paths to field names for UI form validation:
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"] }
}
Enable reportPathAsArray for easier programmatic access:
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] instead of "#/address/city"
Pass includeErrors or excludeErrors as the third argument:
// Only report type mismatches
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });
// Suppress string-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });
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');
}
Map error codes to user-friendly messages:
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]}`,
ARRAY_UNEVALUATED_ITEMS: () => `Array contains items not covered by any schema`,
OBJECT_UNEVALUATED_PROPERTIES: (d) => `${d.params[0]} is not allowed by the schema`,
COLLECT_EVALUATED_DEPTH_EXCEEDED: (d) => `Schema nesting too deep (max ${d.params[0]})`,
MAX_RECURSION_DEPTH_EXCEEDED: (d) => `Recursion depth exceeded (max ${d.params[0]})`,
};
function toFriendlyMessage(detail: SchemaErrorDetail): string {
const fn = friendlyMessages[detail.code];
return fn ? fn(detail) : detail.message;
}
For fail-fast scenarios:
const validator = ZSchema.create({ breakOnFirstError: true });
This reports only the first error encountered, reducing noise during iterative fixing.
The keyword field tells you which schema keyword triggered the error — useful for categorizing errors programmatically:
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;
}
}
}
For the full error code list with descriptions, see the validating-json-data skill's error-codes reference.
Weekly Installs
50
Repository
GitHub Stars
342
First Seen
Feb 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot50
mcpjam10
claude-code10
junie10
windsurf10
zencoder10
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
127,000 周安装
path | `string | Array<string |
keyword | string? | Schema keyword that caused the error ("type", "required", etc.) |
inner | SchemaErrorDetail[]? | Sub-errors from combinators (anyOf, oneOf, not) |
schemaPath | `Array<string | number>?` |
schemaId | string? | Schema ID if present |
title | string? | Schema title if present |
description | string? | Schema description if present |