Server errors
Map API validation failures onto the same issue model as client validators. Server issues participate in form validity until cleared, and can drive field meta + feedback.
System standout: protocol helper
Prefer mapServerErrorBody from @sometic/forms/server for { errors: [{ path|field, code, message }] } and flat maps, then form.setServerErrors(...). With App Shell, bindMutationForm wires this on mutation failure.
Setting errors
import { createIssue } from "@sometic/validation";
form.setServerErrors([
createIssue("server", "Email is already registered", { path: "email" }),
createIssue("taken", "Username is taken", { path: "username" }),
]);Behavior:
- Issues are copied into internal
serverIssues. - For each issue with a
path, the field meta is updated (samecodereplaced, then appended). - If feedback
erroris enabled and there is at least one issue, feedback becomes kind"error"with the first message (or"Server rejected the submission."). validateForm/ submit treat any remainingserverIssuesas invalid even when field validators pass.
Clearing
form.clearServerErrors(); // all
form.clearServerErrors(["email"]); // by pathWhen clearing by path, field issues are stripped only when issue.code === "server". Custom codes such as "taken" remain on the field until you revalidate, clearErrors, or replace them with a new setServerErrors cycle. Prefer code: "server" when you want path clears to remove them automatically.
Client external errors
form.setErrors([createIssue("conflict", "Fix the highlighted fields", { path: "email" })]);
form.clearErrors();
form.clearErrors(["email"]);setErrors feeds clientIssues (not tagged as server). Use for cross-field messages you compute outside validators.
Reading issues
form.getIssues(); // fields + client + server
form.getFieldIssues("email");
form.getFieldMeta("email").issues;
form.getFeedback(); // kind + message + issuesTypical submit flow
const submit = form.handleSubmit({
onValid: async (values) => {
const response = await fetch("/api/register", {
method: "POST",
body: JSON.stringify(values),
});
if (response.status === 422) {
const body = (await response.json()) as {
errors: Array<{ path: string; message: string }>;
};
form.setServerErrors(
body.errors.map((error) =>
createIssue("server", error.message, { path: error.path }),
),
);
return;
}
form.clearServerErrors();
},
});Clear server errors when the user edits a path if you want optimistic recovery:
form.subscribe(() => {
/* or inside setValue wrappers */
});
// simpler: on field change
form.clearServerErrors(["email"]);Interaction with field validation
After a successful client revalidation, field issues refresh from validators but retain server issues that still match an entry in serverIssues (path + code + message). Explicitly clearServerErrors when the backend state is obsolete.
Accessibility
Announce server failures with announceFormErrors and focus the first path via focusFirstInvalid from @sometic/forms/a11y. Keep messages actionable and tied to visible fields.