Форматирование ошибок в Zod строится вокруг унифицированной структуры
ZodError, которая обеспечивает предсказуемость при
обработке результатов валидации и позволяет трансформировать внутренние
ошибки схемы в удобный для отображения или логирования формат.
Основой системы является массив issues, где каждая
ошибка описывается отдельным объектом. Такой подход исключает
необходимость анализировать вложенные исключения и упрощает обработку
комплексных схем.
Каждый объект в issues содержит набор стандартных
полей:
code — тип ошибкиpath — путь к значению в проверяемой структуреmessage — текстовое описание ошибкиexpected / received — ожидаемое и
фактическое значение (для части типов)unionErrors — вложенные ошибки при
union-валидацииПример базового объекта:
{
code: "invalid_type",
expected: "string",
received: "number",
path: ["username"],
message: "Expected string, received number"
}
Поле path является ключевым элементом системы
форматирования. Оно представляет путь в виде массива ключей и индексов,
позволяя точно локализовать источник ошибки:
{
path: ["user", "address", "zip"]
}
Внутренний механизм Zod классифицирует ошибки по коду
code, что определяет структуру дополнительных полей:
invalid_type — несоответствие типа данныхinvalid_literal — значение не совпадает с
литераломtoo_small — значение меньше допустимогоtoo_big — значение больше допустимогоinvalid_union — ошибка объединённого типаcustom — пользовательская ошибкаКаждый тип формирует собственный набор метаданных, которые используются при форматировании.
Метод toString() формирует строковое представление всех
ошибок:
const result = schema.safeParse(data);
if (!result.success) {
console.log(result.error.toString());
}
Формат вывода агрегирует все issues в последовательность строк, но не структурирует их по вложенности, что делает его пригодным только для отладки.
Метод flatten() преобразует дерево ошибок в плоскую
структуру:
const result = schema.safeParse(data);
if (!result.success) {
const flat = result.error.flatten();
}
Результат имеет форму:
{
formErrors: [],
fieldErrors: {
username: ["Required"],
"address.city": ["Invalid value"]
}
}
Особенности:
formErrors содержит ошибки без путиfieldErrors группирует ошибки по ключам верхнего
уровняТакой формат широко используется в UI-слое, поскольку позволяет напрямую привязывать ошибки к полям формы.
Метод format() сохраняет структуру данных и отображает
ошибки в виде вложенного дерева:
const result = schema.safeParse(data);
if (!result.success) {
const formatted = result.error.format();
}
Пример результата:
{
username: {
_errors: ["Required"]
},
address: {
city: {
_errors: ["Invalid city"]
}
}
}
Особенности:
_errorsПри использовании z.union() структура ошибок становится
вложенной:
const schema = z.union([
z.string(),
z.number()
]);
При несоответствии возникает invalid_union, содержащий
массив unionErrors:
{
code: "invalid_union",
unionErrors: [ZodError, ZodError]
}
Каждый элемент unionErrors представляет отдельную
попытку проверки альтернативной схемы.
Форматирование таких ошибок требует рекурсивного обхода:
Система форматирования поддерживает глобальную настройку через
setErrorMap:
import { z } from "zod";
z.setErrorMap((issue, ctx) => {
return { message: "Ошибка валидации" };
});
Параметры callback:
issue — объект ошибкиctx — контекст (включая дефолтное сообщение)Пример более сложной логики:
z.setErrorMap((issue, ctx) => {
if (issue.code === "invalid_type") {
return { message: `Ожидался ${issue.expected}` };
}
return { message: ctx.defaultError };
});
Это позволяет стандартизировать формат ошибок без изменения схем.
Ошибки, созданные вручную, имеют код custom:
z.string().refine((val) => val.length > 3, {
message: "Слишком короткое значение"
});
В superRefine возможна генерация нескольких ошибок:
z.object({
password: z.string()
}).superRefine((data, ctx) => {
if (data.password.length < 8) {
ctx.addIssue({
code: "custom",
path: ["password"],
message: "Пароль слишком короткий"
});
}
});
Такие ошибки полностью интегрируются в стандартный механизм форматирования.
При передаче ошибок через API часто используется ручное преобразование:
const result = schema.safeParse(data);
if (!result.success) {
const serialized = result.error.issues.map(issue => ({
path: issue.path.join("."),
message: issue.message,
code: issue.code
}));
}
Такой формат:
Путь path может содержать:
Пример:
["users", 0, "email"]
При форматировании часто применяется преобразование в строку:
users.0.email
Это упрощает:
При сложных схемах полезно выполнять агрегацию:
Такое представление позволяет:
Ошибки внутри массивов используют числовые индексы:
users: [
{
name: "Valid"
},
{
name: 123
}
]
Результирующий путь:
["users", 1, "name"]
Форматирование может преобразовывать такие ошибки в:
users[1].name
Если поле содержит несколько нарушений, issues содержит
несколько записей с одинаковым path.
Пример:
Форматирование:
При использовании transform() ошибки возникают до или
после преобразования в зависимости от цепочки:
z.string()
.transform(val => Number(val))
Если исходное значение не строка, ошибка фиксируется на этапе входа и не доходит до трансформации.
Форматирование часто дополняется переводом сообщений через
errorMap:
Пример:
Expected string → Ожидалась строка
Required → Обязательное поле
Форматирование ошибок в Zod фактически определяет контракт между слоями системы:
Структура ZodError обеспечивает единый формат
взаимодействия независимо от сложности схемы и глубины вложенности
данных