handling-validation-errors について
z-schema の検証エラーを扱う開発者向けスキルです。エラー詳細の捕捉、確認、絞り込み、変換を行い、プログラム処理やフォーム表示に活用する方法を説明します。
主な機能
- 01例外または安全モードでエラーを捕捉
- 02ネストしたエラー詳細の走査
- 03パスのフォームフィールドへの対応付け
- 04エラーコードによる絞り込み
- 05エラーメッセージのカスタマイズ
活用シーン
- 01フォームでのスキーマエラー表示
- 02ネストした anyOf や oneOf エラーの処理
- 03選択した検証エラーのみの表示
- 04最初のエラーで検証を停止
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zaggino/ z-schema --skill handling-validation-errorsインストール後、Claude Code で/handling-validation-errorsを入力して読み込まれたか確認します。
詳細
z-schema での検証エラーの処理
z-schema は、.details 配列に SchemaErrorDetail を含む ValidateError オブジェクトとして検証エラーを報告します。このスキルでは、これらのエラーの確認、絞り込み、マッピング、表示方法について説明します。
エラーの構造
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';
ValidateError は Error を継承します。
| プロパティ | 型 | 説明 |
|---|---|---|
.name | string | 常に 'ValidateError' です |
.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? | エラーの原因となったスキーマキーワード("type"、"required" など) |
inner | SchemaErrorDetail[]? | コンビネーター(anyOf、oneOf、not)によるサブエラー |
schemaPath | Array<string | number>? | 制約までのスキーマ内のパス |
schemaId | string? | スキーマ ID がある場合 |
title | string? | スキーマの title がある場合 |
description | string? | スキーマの description がある場合 |
エラーの捕捉
try/catch(デフォルトモード)
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}`);
}
}
}
セーフモード(try/catch なし)
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 Pointer のパスをフィールド名に変換し、UI フォームの検証に使用します。
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"] }
}
配列パスを使用する
プログラムからアクセスしやすくするには、reportPathAsArray を有効にします。
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] instead of "#/address/city"
エラーの絞り込み
呼び出しごとの絞り込み
3 番目の引数として includeErrors または excludeErrors を渡します。
// 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');
}
カスタムエラーメッセージ
エラーコードをユーザーフレンドリーなメッセージにマッピングします。
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;
}
最初のエラーで停止する
フェイルファストが必要な場合に使用します。
const validator = ZSchema.create({ breakOnFirstError: true });
これにより、最初に検出されたエラーのみが報告され、反復的な修正時のノイズを減らせます。
keyword フィールドの使用
keyword フィールドは、エラーの原因となったスキーマキーワードを示します。プログラムでエラーを分類する際に便利です。
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;
}
}
}
参考ファイル
説明付きのエラーコード一覧については、validating-json-data スキルのエラーコードリファレンスを参照してください。