Skip to content
Effect Days 2026 Get your ticket

SchemaIssue

Describes problems found while decoding, encoding, or checking data with schemas.

An Issue records what failed and, for nested data, where the failure happened. The Schema system uses these values for missing keys, unexpected keys, invalid types, invalid values, failed filters, failed transformations, and alternatives that did not match. This module also formats issues.

22 exports Added in v3.10.0 Source

Formatting

CheckHook type

Added in v4.0.0 Source

Callback type used to format Filter issues into strings.

When to use

Use when customizing how makeFormatterStandardSchemaV1 renders filter failures.

Details

  • Returns string to override the message, or undefined to fall back to the default formatting.

See

Signature

type CheckHook = (issue: Filter) => string | undefined

Returns the built-in CheckHook used by default formatters.

When to use

Use as the default filter renderer when customizing only the LeafHook.

Details

  • Looks for a message annotation on the inner issue first, then on the filter itself.
  • Returns undefined when no annotation is found, causing the formatter to fall back to "Expected <filter>" or, when the filter reports input, "Expected <filter>, got <input>".

See

Signature

declare const defaultCheckHook: CheckHook

Returns the built-in LeafHook used by default formatters.

When to use

Use as the default leaf renderer when customizing only the CheckHook.

Details

  • Checks for a message annotation first; returns it if present.
  • For InvalidValue, an expected annotation uses the standard expected value message and includes reported input when available.
  • Otherwise generates a default message per _tag. When the issue reports input, the message includes its formatted value where applicable:
    • InvalidType"Expected <type>" or "Expected <type>, got <input>"
    • InvalidValue"Expected a valid value" or "Invalid data <input>"
    • MissingKey"Missing key"
    • UnexpectedKey"Expected no excess property" or "Unexpected key with value <input>"
    • Forbidden"Forbidden operation"
    • OneOf"Expected exactly one member to match" or "Expected exactly one member to match the input <input>"

See

Signature

declare const defaultLeafHook: LeafHook

Example

(Formatting Standard Schema issues with defaultLeafHook)

import { SchemaIssue } from "effect"
const formatter = SchemaIssue.makeFormatterStandardSchemaV1({
leafHook: SchemaIssue.defaultLeafHook
})
formatter(new SchemaIssue.MissingKey(undefined)) // => { issues: [{ path: [], message: "Missing key" }] }

Formatter interface

Added in v4.0.0 Source

A function type that converts an Issue into a formatted representation. Specialisation of the generic Formatter from Formatter.ts with Value fixed to Issue.

See

Signature

interface Formatter<out Format> extends Formatter<Issue, Format> {
(value: Issue): Format;
}

LeafHook type

Added in v4.0.0 Source

Callback type used to format Leaf issues into strings.

When to use

Use when customizing how makeFormatterStandardSchemaV1 renders terminal issues.

See

Signature

type LeafHook = (issue: Leaf) => string

Creates a Formatter that converts an Issue into a human-readable multi-line string.

When to use

Use when you need to format a SchemaIssue.Issue as error messages for logging, CLI output, or developer-facing diagnostics.

Details

  • Flattens the issue tree into { message, path } entries using defaultLeafHook and defaultCheckHook.
  • Includes reported input in default messages when the node producing the message has an input field.
  • Each entry is rendered as "<message>" or "<message>\n at <path>".
  • Multiple entries are joined with newlines.

Gotchas

Formatting an issue can disclose input retained with reportInput: true. Wrapper inputs are not inherited by child messages, and custom messages are returned unchanged.

See

Signature

declare function makeFormatterDefault(): Formatter<string>

Example

(Formatting an issue as a string)

import { SchemaIssue } from "effect"
const formatter = SchemaIssue.makeFormatterDefault()
formatter(new SchemaIssue.MissingKey(undefined)) // => "Missing key"

Creates a Formatter that produces a StandardSchemaV1.FailureResult.

When to use

Use when you need schema parse errors in Standard Schema V1 format, optionally customizing leaf or check issue rendering.

Details

  • Returns a Formatter<StandardSchemaV1.FailureResult>.
  • Each leaf issue is flattened into { message, path } entries.
  • Pointer paths are accumulated to produce full property paths.
  • Falls back to defaultLeafHook / defaultCheckHook when no hooks are provided.
  • Default messages include reported input when the issue that produces the message has an input field. The returned Standard Schema issues do not receive an input field.

Gotchas

Reported input can appear inside the Standard Schema message string even though it is not exposed as a separate property. Custom hooks control their complete message and are not modified.

See

Signature

declare function makeFormatterStandardSchemaV1(options?: {
readonly checkHook?: CheckHook;
readonly leafHook?: LeafHook;
}): Formatter<FailureResult>

Example

(Creating a Standard Schema V1 formatter)

import { SchemaIssue } from "effect"
const formatter = SchemaIssue.makeFormatterStandardSchemaV1()
formatter(new SchemaIssue.MissingKey(undefined)).issues[0].message // => "Missing key"

Guards

hasInput

Added in v4.0.0 Source

Returns true when an issue contains an input reported by the schema parser.

When to use

Use when reading Issue.input, especially when undefined is a valid input value.

Details

Reported input is stored as an own property. This guard checks for that property and narrows input from optional to required.

See

  • Issue for the complete issue model

Signature

declare function hasInput(issue: Issue): issue is Issue & {
readonly input: unknown;
}

Example

(Reading a reported input)

import { Result, Schema, SchemaIssue } from "effect"
const result = Schema.decodeUnknownResult(Schema.String)(1, { reportInput: true })
if (Result.isFailure(result) && SchemaIssue.hasInput(result.failure.issue)) {
result.failure.issue.input // => 1
}

isIssue

Added in v4.0.0 Source

Returns true if the given value is an Issue.

When to use

Use when you need to narrow an unknown value to Issue in error-handling code, such as distinguishing an Issue from other error types in a catch-all handler.

Details

  • Checks for the internal TypeId brand on the value.

See

Signature

declare function isIssue(u: unknown): u is Issue

Example

(Type-guarding an unknown error)

import { SchemaIssue } from "effect"
const issue = new SchemaIssue.MissingKey(undefined)
SchemaIssue.isIssue(issue) // => true
SchemaIssue.isIssue("not an issue") // => false

Models

AnyOf

Added in v4.0.0 Source

Represents a schema issue produced when a value does not match any member of a union schema.

When to use

Use when you need to inspect which union members were attempted and why each failed.

Details

  • ast is the Union AST node.
  • issues contains the per-member failures.

Gotchas

issues is empty when no union member was applicable. In that case, the default formatter reports the expected type for the union and appends ", got <input>" when input is reported.

See

  • OneOf — the opposite: too many members matched
  • Composite — groups multiple issues under a non-union schema

Signature

declare class AnyOf extends Base {
constructor(ast: Union, issues: readonly Array<Issue>, input?: unknown, options?: ParseOptions);
readonly _tag: "AnyOf";
readonly ast: Union;
readonly issues: readonly Array<Issue>;
}

Composite

Added in v3.10.0 Source

Represents a schema issue that groups multiple child issues under a single schema node.

When to use

Use when you need to walk the issue tree for struct/tuple schemas that collect all field errors rather than failing on the first.

Details

  • issues is a non-empty readonly array (at least one child).
  • Formatters flatten Composite by recursing into each child.

See

  • AnyOf — used for union no-match errors (similar but different semantics)
  • Pointer — adds path context to individual issues

Signature

declare class Composite extends Base {
constructor(ast: AST, issues: readonly [Issue, Issue], input?: unknown, options?: ParseOptions);
readonly _tag: "Composite";
readonly ast: AST;
readonly issues: readonly [Issue, Issue];
}

Encoding

Added in v4.0.0 Source

Represents a schema issue produced when a schema transformation (encode/decode step) fails.

When to use

Use when you need to inspect failures from Schema.decodeTo / Schema.encodeTo transformations.

Details

  • ast is the AST node for the transformation that failed.
  • issue is the inner issue describing the failure.

See

  • Filter — failure from a refinement check (not a transformation)
  • Composite — multiple issues from a single schema node

Signature

declare class Encoding extends Base {
constructor(ast: AST, issue: Issue, input?: unknown, options?: ParseOptions);
readonly _tag: "Encoding";
readonly ast: AST;
readonly issue: Issue;
}

Filter

Added in v4.0.0 Source

Represents a schema issue produced when a schema filter (refinement check) fails.

When to use

Use when you need to inspect a schema issue that records which refinement check rejected the value.

Details

  • filter is the AST filter node that produced this issue.
  • issue is the inner issue describing the failure reason.

See

  • Leaf — terminal issue types that commonly appear as the inner issue
  • CheckHook — formatter hook for Filter issues

Signature

declare class Filter extends Base {
constructor(filter: Filter<any>, issue: Issue, input?: unknown, options?: ParseOptions);
readonly _tag: "Filter";
readonly filter: Filter<unknown>;
readonly issue: Issue;
}

Example

(Matching a Filter issue)

import { SchemaAST, SchemaIssue } from "effect"
const formatIssue = SchemaIssue.makeFormatterDefault()
function describe(issue: SchemaIssue.Issue): string {
if (issue._tag === "Filter") {
return `Filter failed: ${formatIssue(issue.issue)}`
}
return formatIssue(issue)
}
const issue = new SchemaIssue.Filter(
SchemaAST.isPattern(/^valid$/),
new SchemaIssue.InvalidValue()
)
describe(issue) // => `Filter failed: Expected a valid value`

Forbidden

Added in v3.10.0 Source

Represents a schema issue produced when a forbidden operation is encountered during parsing, such as an asynchronous Effect running inside Schema.decodeUnknownSync.

When to use

Use when you need to detect that a schema requires async execution but was run synchronously.

Details

  • annotations optionally carries a message string.
  • The default formatter renders this as "Forbidden operation".

See

  • InvalidValue — for value-constraint failures (not operation failures)

Signature

declare class Forbidden extends Base {
constructor(annotations: Issue | undefined, input?: unknown, options?: ParseOptions);
readonly _tag: "Forbidden";
readonly annotations: Issue | undefined;
}

Example

(Creating a Forbidden issue)

import { SchemaIssue } from "effect"
const formatIssue = SchemaIssue.makeFormatterDefault()
const issue = new SchemaIssue.Forbidden(
{ message: "async operation not allowed in sync context" }
)
formatIssue(issue) // => "async operation not allowed in sync context"

InvalidType

Added in v4.0.0 Source

Represents a schema issue produced when the runtime type of the input does not match the type expected by the schema.

When to use

Use when you need to detect basic type mismatches, such as a wrong primitive or null where an object was expected.

Details

  • ast is the schema node that expected a different type.
  • The default formatter renders this as "Expected <type>", adding ", got <input>" when the issue reports an input.

See

  • InvalidValue — the input has the right type but fails a value constraint

Signature

declare class InvalidType extends Base {
constructor(ast: AST, input?: unknown, options?: ParseOptions);
readonly _tag: "InvalidType";
readonly ast: AST;
}

Example

(Formatting a type mismatch)

import { Schema, SchemaIssue } from "effect"
const formatIssue = SchemaIssue.makeFormatterDefault()
const issue = new SchemaIssue.InvalidType(Schema.String.ast)
formatIssue(issue) // => "Expected string"

InvalidValue

Added in v4.0.0 Source

Represents a schema issue produced when the input has the correct type but its value violates a constraint (e.g. a string that is too short, a number out of range).

When to use

Use when you need to detect constraint violations from Schema.filter, Schema.minLength, Schema.greaterThan, or similar checks.

Details

  • A message annotation is returned unchanged and takes precedence over all other default formatting.
  • Without message, an expected annotation is formatted as "Expected <expected>", adding ", got <input>" when input is reported.
  • Without either annotation, the default formatter renders "Expected a valid value", or "Invalid data <input>" when input is reported.

See

  • InvalidType — the input has the wrong type entirely
  • Filter — composite wrapper when a schema filter produces this issue

Signature

declare class InvalidValue extends Base {
constructor(annotations?: Issue, input?: unknown, options?: ParseOptions);
readonly _tag: "InvalidValue";
readonly annotations: Issue | undefined;
}

Example

(Returning InvalidValue from a custom filter)

import { SchemaIssue } from "effect"
const formatIssue = SchemaIssue.makeFormatterDefault()
const issue = new SchemaIssue.InvalidValue({ message: "must not be empty" })
formatIssue(issue) // => "must not be empty"

Issue type

Added in v4.0.0 Source

The root discriminated union of all validation error nodes.

When to use

Use when typing the error channel in Effect<A, Issue, R> results from schema parsing, or when writing custom formatters or issue-tree walkers.

Details

Every node has a _tag field for pattern-matching. The union includes both terminal Leaf types and composite types that wrap inner issues: Filter, Encoding, Pointer, Composite, AnyOf. Use makeFormatterDefault when a human-readable representation is needed. When parsing with reportInput: true, value-bearing issues expose the rejected value through an enumerable input field. Built-in formatters may include reported input in default messages. This is not a general sanitization boundary: paths, ASTs, union successes, and custom annotations or messages are preserved as supplied and remain the caller's responsibility.

See

  • Leaf — the terminal subset
  • isIssue — type guard
  • hasInput — checks whether an issue reports an input

Signature

type Issue = Leaf | Filter | Encoding | Pointer | Composite | AnyOf

Leaf type

Added in v4.0.0 Source

Union of all terminal (leaf) issue types that have no inner Issue children.

When to use

Use when constraining formatter hooks to only handle terminal nodes or when pattern matching on the _tag of an issue and only leaf nodes matter.

Details

Members: InvalidType, InvalidValue, MissingKey, UnexpectedKey, Forbidden, OneOf.

See

  • Issue — the full union including composite nodes
  • LeafHook — formatter hook that operates on Leaf values

Signature

type Leaf = InvalidType | InvalidValue | MissingKey | UnexpectedKey | Forbidden | OneOf

MissingKey

Added in v4.0.0 Source

Represents a schema issue produced when a required key or tuple index is missing from the input.

When to use

Use when you need to detect absent fields in struct/tuple validation.

Details

  • annotations may contain a custom messageMissingKey for formatting.

See

  • Pointer — wraps this issue with the missing key's path
  • UnexpectedKey — the opposite case (extra key present)

Signature

declare class MissingKey extends Base {
constructor(annotations: Key<unknown> | undefined);
readonly _tag: "MissingKey";
readonly annotations: Key<unknown> | undefined;
}

OneOf

Added in v4.0.0 Source

Represents a schema issue produced when a value matches multiple members of a union that is configured to allow exactly one match (oneOf mode).

When to use

Use when you need to detect ambiguous union matches when oneOf validation is enabled.

Details

  • ast is the Union AST node.
  • successes lists the AST nodes of each member that accepted the input.
  • The default formatter renders this as "Expected exactly one member to match", or "Expected exactly one member to match the input <input>" when input is reported.

See

  • AnyOf — the opposite: no members matched

Signature

declare class OneOf extends Base {
constructor(ast: Union, successes: readonly Array<AST>, input?: unknown, options?: ParseOptions);
readonly _tag: "OneOf";
readonly ast: Union;
readonly successes: readonly Array<AST>;
}

Pointer

Added in v3.10.0 Source

Wraps an inner Issue with a property-key path, indicating where in a nested structure the error occurred.

When to use

Use when you need to walk the issue tree to accumulate path segments for error reporting.

Details

  • path is an array of property keys (strings, numbers, or symbols).
  • Formatters concatenate nested Pointer paths into a single path like ["a"]["b"][0].

See

  • Composite — groups multiple issues under one schema node

Signature

declare class Pointer extends Base {
constructor(path: readonly Array<PropertyKey>, issue: Issue);
readonly _tag: "Pointer";
readonly issue: Issue;
readonly path: readonly Array<PropertyKey>;
}

Represents a schema issue produced when an input object or tuple contains a key/index not declared by the schema.

When to use

Use when you need to detect excess properties during strict struct/tuple validation.

Details

  • ast is the schema that was being validated against.
  • annotations on ast may contain a custom messageUnexpectedKey.
  • The default formatter renders this as "Expected no excess property", or "Unexpected key with value <input>" when the issue reports an input.

See

  • MissingKey — the opposite case (required key absent)
  • Pointer — wraps this issue with the unexpected key's path

Signature

declare class UnexpectedKey extends Base {
constructor(ast: AST, input?: unknown, options?: ParseOptions);
readonly _tag: "UnexpectedKey";
readonly ast: AST;
}