Read the result
safeParse returns success, data, and issues. This is how you use them on a form.
safeParse does not throw if the data is bad. It returns an object so you can decide what to show.
const result = await UserSemantic.safeParse(input); if (!result.success) { return Response.json(result, { status: 400 });} // result.data is typed as the output of your Zod schemasaveUser(result.data);The three parts
| Field | What it means |
|---|---|
success | false only if some issue has severity: "error". |
data | The data already checked by Zod. It is there if the shape passed, even if a semantic rule failed. |
issues | Every problem in one array. Zod first, then EDcheck. |
If Zod rejects the shape, data is missing. If Zod passed and EDcheck added a warning, data is there and success can still be true.
What an issue looks like
{ path: ["fullName"], code: "semantic", severity: "error", message: "Semantic rule \"fullName\" failed", outcome: "fail", ruleId: "fullName", probability: 0.18, thresholds: { pass: 0.7, fail: 0.7 },}| Field | What it is for |
|---|---|
path | Which field should show the error. Use it to paint the input red. |
code | semantic if meaning failed. semantic_unavailable if the AI did not answer. |
severity | error, warning, or info. |
message | Text for the user or for your logs. |
ruleId | Which rule fired. Useful so you do not show the same error twice. |
probability | How sure the AI was (yes/no rules). |
Show errors on the form
Group issues by the first value in path. Then each input can show its own message.
function messagesByField(issues: { path: Array<string | number>; message: string }[]) { const map = new Map<string, string[]>(); for (const issue of issues) { const key = String(issue.path[0] ?? ""); const list = map.get(key) ?? []; list.push(issue.message); map.set(key, list); } return map;}Thresholds (how strict the AI is)
The AI does not say a hard yes or no. It gives a number between 0 and 1. EDcheck cuts it like this:
p >= pass→ pass (no issue)fail <= p < pass→ warningp < fail→ fail
By default pass and fail are 0.7. That means: 70% or more passes, below 70% fails. There is no warning band unless you open one.
const edcheck = createEDcheck({ provider, thresholds: { pass: 0.8, fail: 0.4 },});With that: 0.8 or more passes, between 0.4 and 0.8 is a warning, below 0.4 fails.
You can set thresholds on the instance, the schema, or one rule. The most specific one wins: rule > schema > instance > the default.