重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
safe-action-validation-errors by next-safe-action/skills
npx skills add https://github.com/next-safe-action/skills --skill safe-action-validation-errors.inputSchema() 时自动触发returnValidationErrors() 触发(例如,“邮箱已被占用”)两者在客户端产生相同的错误结构。
镜像模式结构,每个层级都包含 _errors 数组:
// 对于模式:z.object({ email: z.string().email(), address: z.object({ city: z.string() }) })
{
_errors: ["表单级错误"], // 根级错误
email: { _errors: ["无效的邮箱地址"] }, // 字段错误
address: {
_errors: ["地址部分错误"],
city: { _errors: ["城市为必填项"] }, // 嵌套字段错误
},
}
抛出一个 ActionServerValidationError,框架会捕获并将其作为 result.validationErrors 返回。 — 它总是抛出异常。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
"use server";
import { z } from "zod";
import { returnValidationErrors } from "next-safe-action";
import { actionClient } from "@/lib/safe-action";
const registerSchema = z.object({
email: z.string().email(),
username: z.string().min(3),
});
export const register = actionClient
.inputSchema(registerSchema)
.action(async ({ parsedInput }) => {
// 在模式验证通过后检查业务规则
const existingUser = await db.user.findByEmail(parsedInput.email);
if (existingUser) {
returnValidationErrors(registerSchema, {
email: { _errors: ["此邮箱已被注册"] },
});
}
const existingUsername = await db.user.findByUsername(parsedInput.username);
if (existingUsername) {
returnValidationErrors(registerSchema, {
username: { _errors: ["此用户名已被占用"] },
});
}
// 两项检查均通过 — 创建用户
const user = await db.user.create(parsedInput);
return { id: user.id };
});
在顶层使用 _errors 表示表单范围的错误:
returnValidationErrors(schema, {
_errors: ["您每天只能创建 5 个帖子"],
});
// 格式化格式(默认)
{result.validationErrors?.email?._errors?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
// 根级错误
{result.validationErrors?._errors?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
// 扁平化格式
{result.validationErrors?.fieldErrors?.email?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
// 表单级错误(扁平化)
{result.validationErrors?.formErrors?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
每周安装量
58
代码仓库
首次出现
2026年3月6日
安全审计
安装于
cursor57
gemini-cli57
amp57
github-copilot57
codex57
kimi-cli57
.inputSchema()returnValidationErrors() in server code (e.g., "email already taken")Both produce the same error structure on the client.
Mirrors the schema structure with _errors arrays at each level:
// For schema: z.object({ email: z.string().email(), address: z.object({ city: z.string() }) })
{
_errors: ["Form-level error"], // root errors
email: { _errors: ["Invalid email address"] }, // field errors
address: {
_errors: ["Address section error"],
city: { _errors: ["City is required"] }, // nested field errors
},
}
Throws a ActionServerValidationError that the framework catches and returns as result.validationErrors. It never returns — it always throws.
"use server";
import { z } from "zod";
import { returnValidationErrors } from "next-safe-action";
import { actionClient } from "@/lib/safe-action";
const registerSchema = z.object({
email: z.string().email(),
username: z.string().min(3),
});
export const register = actionClient
.inputSchema(registerSchema)
.action(async ({ parsedInput }) => {
// Check business rules after schema validation passes
const existingUser = await db.user.findByEmail(parsedInput.email);
if (existingUser) {
returnValidationErrors(registerSchema, {
email: { _errors: ["This email is already registered"] },
});
}
const existingUsername = await db.user.findByUsername(parsedInput.username);
if (existingUsername) {
returnValidationErrors(registerSchema, {
username: { _errors: ["This username is taken"] },
});
}
// Both checks passed — create the user
const user = await db.user.create(parsedInput);
return { id: user.id };
});
Use _errors at the top level for form-wide errors:
returnValidationErrors(schema, {
_errors: ["You can only create 5 posts per day"],
});
// Formatted shape (default)
{result.validationErrors?.email?._errors?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
// Root-level errors
{result.validationErrors?._errors?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
// Flattened shape
{result.validationErrors?.fieldErrors?.email?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
// Form-level errors (flattened)
{result.validationErrors?.formErrors?.map((error) => (
<p key={error} className="text-red-500">{error}</p>
))}
Weekly Installs
58
Repository
First Seen
Mar 6, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
cursor57
gemini-cli57
amp57
github-copilot57
codex57
kimi-cli57
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
125,600 周安装