Error Formatting
When decoding or encoding fails, Schema does not throw away the details. It
produces a SchemaIssue.Issue — a recursive tree that records what went wrong
and where in the input. You can render it as a human-readable string, walk it
to build custom output, or convert it to a Standard Schema failure result.
import { Schema, Result } from "effect"
const Person = Schema.Struct({ name: Schema.String, age: Schema.Number})
// `decodeUnknownResult` returns Result<Person, SchemaIssue.Issue> — failures// are data, not exceptions. `{ errors: "all" }` collects every problem at once.const result = Schema.decodeUnknownResult(Person, { errors: "all" })({ name: 42})
if (Result.isFailure(result)) { // Every Issue has a toString(), so String(issue) is a ready-made message. console.error(String(result.failure))}Where the issue lives
Section titled “Where the issue lives”How you reach the issue depends on the runner you chose in basic usage:
decodeUnknownResult/decodeUnknownOption/decodeUnknownExit— the failure channel carries theSchemaIssue.Issuedirectly.decodeUnknownEffect— the Effect fails with aSchemaIssue.Issuein its error channel.decodeUnknownSync— throws a plainErrorwhosemessageis the formatted string and whosecauseholds theSchemaIssue.Issue.
import { Schema } from "effect"
const decode = Schema.decodeUnknownSync(Schema.Number)
try { decode("not a number")} catch (err) { if (err instanceof Error) { // err.message is the formatted string; err.cause is the structured tree. console.error(err.message) // Expected number, got "not a number" }}The default string formatter
Section titled “The default string formatter”Calling String(issue) (or issue.toString()) uses the default formatter. It
renders each leaf failure with its message and the property path at which it
occurred.
import { Schema, Effect } from "effect"
const Person = Schema.Struct({ name: Schema.String, age: Schema.Number})
const program = Schema.decodeUnknownEffect(Person, { errors: "all" })({}).pipe( // The error channel holds a SchemaIssue.Issue; format it for logging. Effect.catch((issue) => Effect.logError(String(issue))))When the input is missing both keys with { errors: "all" }, the formatted
output names each failing path:
Missing key at ["name"]Missing key at ["age"]Inspecting the issue tree
Section titled “Inspecting the issue tree”For programmatic handling — mapping failures to your own error type, grouping by
field, building a localized message — pattern-match on the issue’s _tag. The
tree is a discriminated union; the terminal leaf nodes are the ones you usually
care about.
import { Schema, SchemaIssue } from "effect"
// Describe an issue in your own words by matching on its tag.function describe(issue: SchemaIssue.Issue): string { switch (issue._tag) { case "MissingKey": return "a required field is missing" case "InvalidType": return "a field has the wrong type" case "InvalidValue": return "a field failed a constraint" default: // Composite nodes (Pointer, Composite, Filter, ...) wrap inner issues. return String(issue) }}
void describe // illustrativeThe composite nodes — Pointer, Composite, Filter, Encoding, AnyOf —
wrap inner issues to add context such as a path segment or the filter that
failed. SchemaIssue.getActual(issue) extracts the offending input value where
one is available.
Standard Schema output
Section titled “Standard Schema output”To integrate with tooling that speaks the
Standard Schema spec — form
libraries, validators — build a formatter that produces a FailureResult of
{ message, path } entries instead of a string.
import { Schema, SchemaIssue, Result } from "effect"
const Login = Schema.Struct({ username: Schema.String.check(Schema.isNonEmpty()), password: Schema.String.check(Schema.isMinLength(8))})
const formatter = SchemaIssue.makeFormatterStandardSchemaV1()
const result = Schema.decodeUnknownResult(Login, { errors: "all" })({ username: "", password: "short"})
if (Result.isFailure(result)) { // { issues: [{ message, path }, ...] } — one entry per failing field. console.error(formatter(result.failure).issues)}Next, see how to define your own typed errors with
Schema.TaggedErrorClass.