Error class hierarchy
Validation modes: Validation modes reference
Every json-tology error extends BaseError. The base class carries a machine-readable code, a human message, a retryable flag, an optional cause chain, and two structured projections (toJson(), flatten()). Every subclass adds domain-specific fields - schema IDs, JSON Pointers, validation errors.
Error constructors follow the universal DX convention: required arguments are positional in canonical order; every optional or contextual field travels in a single trailing options bag typed by an Interface or Type alias. The instance fields described below are unchanged - only the constructor argument shape collapsed into a single options object.
Never throw a bare
new Error()from json-tology code. Pick the appropriate subclass.
The bookstore schemas defined in the Bookstore Domain appear in the catch examples.
BaseError
Defined in. src/errors/BaseError.ts.
Constructor. new BaseError(code, message, options?: BaseErrorOptionsType) where BaseErrorOptionsType = { cause?: Error; retryable?: boolean }. retryable defaults to false when omitted.
/**
* BaseError constructor — the standard Error(message, options) convention.
*
* `message` is the required positional. `code` and all optional fields
* (`cause`, `retryable`) travel in the required options bag.
* `retryable` defaults to `false` when omitted from options.
*/
import { BaseError } from '../../../src/index.js';
const ioFailure = new Error('socket closed');
const bare = new BaseError('human description', { 'code': 'SOMETHING_FAILED' });
const retryable = new BaseError('human description', {
'code': 'SOMETHING_FAILED',
'retryable': true
});
const chained = new BaseError('human description', {
'cause': ioFailure,
'code': 'SOMETHING_FAILED',
'retryable': true
});
console.assert(!bare.retryable);
console.assert(retryable.retryable);
console.assert(chained.cause === ioFailure);
console.log('bare.retryable:', bare.retryable);
console.log('retryable.retryable:', retryable.retryable);
console.log('chained.code:', chained.code);
console.log('chained.cause.message:', (chained.cause as Error).message);
Public surface.
| Member | Type | Notes |
|---|---|---|
code | string | Stable, machine-readable identifier |
message | string | Inherited from Error |
retryable | boolean | Hint to callers about retry safety; set via options.retryable (default false) |
cause | Error | undefined | Standard cause chain; set via options.cause |
toJson() | ErrorJsonInterface | JSON-safe object including the cause chain |
flatten() | ErrorJsonInterface[] | Root-first array of every error in the cause chain |
The code values are exported as constants from src/constants/ERROR_CODES.ts for each subclass.
/**
* BaseError — public surface: code, toJson, flatten
*
* Every json-tology error subclasses BaseError. Catch by class,
* inspect the structured projections. Demonstrates the trust-boundary
* pattern: instantiate against the canonical bookstore ReviewSchema
* and surface the failure as a structured envelope.
*/
import { BaseError } from '../../../src/index.js';
import {
bookstoreEntities, ReviewSchema
} from '../bookstore/index.js';
try {
bookstoreEntities.instantiate(ReviewSchema.$id, {
'body': 'Too',
'rating': 99
});
} catch (error) {
if (error instanceof BaseError) {
console.assert(typeof error.code === 'string');
console.assert(typeof error.toJson() === 'object');
console.assert(Array.isArray(error.flatten()));
console.log('error.code:', error.code);
console.log('error.toJson():', JSON.stringify(error.toJson(), null, 2));
console.log('error.flatten() count:', error.flatten().length);
}
}
SchemaError Runtime
Thrown for. Schema registration and structural problems - missing $id, duplicate anchors, unsupported dialect, structure validation failures.
Constructor. new SchemaError(code, message, options?: SchemaErrorOptionsType) where SchemaErrorOptionsType = { cause?: Error; schemaId?: string }.
/**
* SchemaError constructor — the standard Error(message, options) convention.
*
* `message` is the required positional. `code`, `schemaId`, and `cause`
* travel in the required options bag. The code values come from the
* exported `SCHEMA_ERROR_CODE` map.
*/
import {
SCHEMA_ERROR_CODE, SchemaError
} from '../../../src/index.js';
const schemaId = 'urn:bookstore:Order';
const cause = new Error('vocabulary not registered');
const missingId = new SchemaError('schema is missing $id', { 'code': SCHEMA_ERROR_CODE.MISSING_ID });
const structure = new SchemaError('invalid structure', {
'code': SCHEMA_ERROR_CODE.STRUCTURE_INVALID,
schemaId
});
const dialect = new SchemaError('unsupported dialect', {
cause,
'code': SCHEMA_ERROR_CODE.DIALECT_UNSUPPORTED,
schemaId
});
console.assert(missingId.code === 'SCHEMA_MISSING_ID');
console.assert(structure.schemaId === schemaId);
console.assert(dialect.cause === cause);
console.log('missingId.code:', missingId.code);
console.log('structure.schemaId:', structure.schemaId);
console.log('dialect.cause.message:', dialect.cause?.message);
Adds. schemaId?: string (the offending schema, when known) - exposed as an instance field and set via options.schemaId.
Codes.
| Constant | Value | Notes |
|---|---|---|
SCHEMA_ERROR_CODE.MISSING_ID | SCHEMA_MISSING_ID | |
SCHEMA_ERROR_CODE.INVALID_INPUT | SCHEMA_INVALID_INPUT | |
SCHEMA_ERROR_CODE.NOT_REGISTERED | SCHEMA_NOT_REGISTERED | |
SCHEMA_ERROR_CODE.STRUCTURE_INVALID | SCHEMA_STRUCTURE_INVALID | |
SCHEMA_ERROR_CODE.DUPLICATE_ANCHOR | SCHEMA_DUPLICATE_ANCHOR | |
SCHEMA_ERROR_CODE.DIALECT_UNSUPPORTED | SCHEMA_DIALECT_UNSUPPORTED | |
SCHEMA_ERROR_CODE.VALIDATOR_MISSING | SCHEMA_VALIDATOR_MISSING | |
SCHEMA_ERROR_CODE.COMPUTED_FN_MISSING | COMPUTED_FN_MISSING | |
SCHEMA_ERROR_CODE.COMPUTED_INPUT_FORBIDDEN | COMPUTED_INPUT_FORBIDDEN | |
SCHEMA_ERROR_CODE.DUPLICATE_ID | SCHEMA_DUPLICATE_ID | Thrown by SchemaRegistry when two schemas with the same $id are registered. Detected during set() with enableDuplicateDetection enabled. |
SCHEMA_ERROR_CODE.DUPLICATE_SHAPE | SCHEMA_DUPLICATE_SHAPE | Thrown by SchemaRegistry when a schema with a duplicate canonical shape (same structural hash) is registered. |
SCHEMA_ERROR_CODE.PROPERTY_CHARACTERISTIC_CONFLICT | PROPERTY_CHARACTERISTIC_CONFLICT | Thrown by SchemaRegistry when conflicting property characteristics are registered for the same property. |
/**
* SchemaError — registration / structural problems.
*
* A schema literal missing `$id` cannot be registered. The thrown
* SchemaError carries the `SCHEMA_MISSING_ID` code; `schemaId` is
* undefined because the offending schema has no identifier yet.
*/
import {
JsonTology, SchemaError
} from '../../../src/index.js';
// invalid-input edge: a schema loaded from an untyped source (disk, network)
// arrives as `unknown` — no compile-time `$id` guarantee. Narrowing it at the
// registration boundary triggers the SCHEMA_MISSING_ID runtime guard, the
// negative path `create`'s typed signature would otherwise forbid.
const fromDisk: unknown = { 'type': 'object' };
try {
JsonTology.create({
'baseIri': 'https://bookstore.example',
'schemas': [fromDisk as { readonly '$id': string }]
});
} catch (error) {
if (error instanceof SchemaError) {
console.assert(error.code === 'SCHEMA_MISSING_ID');
console.assert(error.schemaId === undefined);
console.log('error.code:', error.code);
console.log('error.message:', error.message);
console.log('error.schemaId:', error.schemaId);
}
}
GraphError Runtime
Thrown for. Pointer resolution failures, anchor lookup failures, ref resolution failures, dialect or vocabulary issues, recursion-limit hits.
Constructor. new GraphError(code, message, options?: GraphErrorOptionsType) where GraphErrorOptionsType = { cause?: Error; pointer?: string }.
/**
* GraphError constructor — the standard Error(message, options) convention.
*
* `message` is the required positional. `code`, `pointer`, and `cause`
* travel in the required options bag. The code values come from the
* exported `GRAPH_ERROR_CODE` map.
*/
import {
GRAPH_ERROR_CODE, GraphError
} from '../../../src/index.js';
const pointer = '/orderLines/0';
const cause = new Error('referenced schema absent from registry');
const notFound = new GraphError('pointer did not resolve', {
'code': GRAPH_ERROR_CODE.POINTER_NOT_FOUND,
'pointer': '/foo/0'
});
const refUnresolved = new GraphError('cross-schema $ref unresolved', {
cause,
'code': GRAPH_ERROR_CODE.REF_UNRESOLVED,
pointer
});
console.assert(notFound.code === 'POINTER_NOT_FOUND');
console.assert(notFound.pointer === '/foo/0');
console.assert(refUnresolved.cause === cause);
console.log('notFound.code:', notFound.code);
console.log('notFound.pointer:', notFound.pointer);
console.log('refUnresolved.cause.message:', refUnresolved.cause?.message);
Adds. pointer?: string (the JSON Pointer involved in the failure, when applicable) - exposed as an instance field and set via options.pointer.
Codes.
| Constant | Value | Notes |
|---|---|---|
GRAPH_ERROR_CODE.POINTER_INVALID | POINTER_INVALID | |
GRAPH_ERROR_CODE.POINTER_NOT_FOUND | POINTER_NOT_FOUND | |
GRAPH_ERROR_CODE.POINTER_NOT_SCHEMA | POINTER_NOT_SCHEMA | |
GRAPH_ERROR_CODE.ANCHOR_NOT_FOUND | ANCHOR_NOT_FOUND | |
GRAPH_ERROR_CODE.REF_NOT_FOUND | REF_NOT_FOUND | $ref ID cannot be resolved within the graph by RefResolution. Distinct from REF_UNRESOLVED: thrown when in-graph resolution fails after the schema is already loaded (pointer, anchor, and embedded-$id lookups all exhausted). |
GRAPH_ERROR_CODE.REF_UNRESOLVED | REF_UNRESOLVED | Cross-schema $ref points to an IRI not in the registry. Thrown on first use of a schema entry. |
GRAPH_ERROR_CODE.RECURSION_LIMIT | RECURSION_LIMIT | |
GRAPH_ERROR_CODE.DIALECT_UNSUPPORTED | DIALECT_UNSUPPORTED | |
GRAPH_ERROR_CODE.VOCABULARY_UNSUPPORTED | VOCABULARY_UNSUPPORTED | |
GRAPH_ERROR_CODE.ARTIFACT_INVALID | ARTIFACT_INVALID | |
GRAPH_ERROR_CODE.ARTIFACT_STALE | ARTIFACT_STALE | |
GRAPH_ERROR_CODE.CURSOR_CARDINALITY | CURSOR_CARDINALITY | Thrown by Cursor and SchemaCursor when a selection that expects exactly one result contains multiple items. |
GRAPH_ERROR_CODE.INVALID_LANGUAGE_TAG | INVALID_LANGUAGE_TAG | Thrown by SchemaGraphSupport when a language tag does not conform to BCP 47 syntax. |
GRAPH_ERROR_CODE.INVALID_PREDICATE_IRI | INVALID_PREDICATE_IRI | Thrown by PredicateResolver and QuadFactory when a predicate IRI contains invalid characters or is malformed. |
/**
* GraphError — pointer resolution failure.
*
* `subschemaAt` walks a JSON Pointer into the canonical OrderSchema.
* A pointer that does not address an existing fragment surfaces a
* GraphError with code `POINTER_NOT_FOUND` and the offending pointer
* attached.
*/
import { GraphError } from '../../../src/index.js';
import {
bookstoreEntities, OrderSchema
} from '../bookstore/index.js';
try {
bookstoreEntities.subschemaAt(OrderSchema.$id, '/properties/nope');
} catch (error) {
if (error instanceof GraphError) {
console.assert(error.code === 'POINTER_NOT_FOUND');
console.assert(error.pointer === '/properties/nope');
console.log('error.code:', error.code);
console.log('error.pointer:', error.pointer);
console.log('error.message:', error.message);
}
}
InstantiationError Runtime
Thrown for. Validation failure inside instantiate() - the trust-boundary entry point. Carries the full structured error list.
Adds. errors: ValidationErrors (the full ValidationErrors collection).
Codes. Always INSTANTIATION_FAILED at the wrapper level; per-error keyword values appear inside errors.items. Additional keyword codes recorded inside errors.items:
| Constant | Value | When recorded |
|---|---|---|
INSTANTIATION_ERROR_CODE.EXTRA_FORBIDDEN | EXTRA_FORBIDDEN | jt:config.extra: 'forbid' rejects unknown properties |
/**
* InstantiationError — trust-boundary failure on instantiate().
*
* The canonical Bastian-orders-Neverending-Story payload is fed
* through `instantiate()` with two deliberate violations: a
* non-positive total and a zero-quantity line item. The thrown
* InstantiationError carries the full `ValidationErrors` collection.
*/
import { InstantiationError } from '../../../src/index.js';
import {
bookstoreEntities, OrderSchema
} from '../bookstore/index.js';
try {
bookstoreEntities.instantiate(OrderSchema.$id, {
'customerId': 'c1a2b3d4-e5f6-7890-abcd-ef1234567890',
'id': '09f8e7d6-c5b4-3210-9876-543210fedcba',
'items': [{
'bookIsbn': '9783522128001',
'quantity': 0,
'unitPrice': {
'amount': 12.99,
'currency': 'EUR'
}
}],
'placedAt': '2026-04-12T14:23:11Z',
'shippingAddress': {
'city': 'München',
'country': 'DE',
'postalCode': '80331',
'street': 'Reichenbachstraße 14'
},
'total': {
'amount': -5,
'currency': 'EUR'
}
});
} catch (error) {
if (error instanceof InstantiationError) {
console.assert(error.code === 'INSTANTIATION_FAILED');
console.assert(error.errors.length > 0);
console.log('error.code:', error.code);
console.log('error.errors.length:', error.errors.length);
for (const item of error.errors) {
console.log(` path=${item.path} keyword=${item.keyword} message=${item.message}`);
}
}
}
The errors collection is the same ValidationErrors used by validate() - see ValidationErrors views for the full surface.
CoercionError Runtime
Thrown for. Coercion failure, raised by value.cast(), value.convert(), and their registry equivalents (registry.cast, registry.convert) when the coerced data does not satisfy the schema. Carries the full structured error list.
Adds. errors: ValidationErrors (the full ValidationErrors collection describing every constraint that coercion could not satisfy).
Codes.
| Constant | Value | Notes |
|---|---|---|
COERCION_ERROR_CODE.COERCION_FAILED | COERCION_FAILED | Always emitted at the wrapper level |
/**
* CoercionError — raised by the coercion path.
*
* CoercionError mirrors InstantiationError but is thrown when a
* coercion call rejects its input. Demonstrates the public surface
* with a deliberately-constructed instance so the catch shape is
* exercised without depending on a private coercion site.
*/
import { CoercionError } from '../../../src/index.js';
const synthetic = new CoercionError([{
'keyword': 'minimum',
'message': 'must be >= 0',
'params': {},
'path': '/total/amount'
}], { 'code': 'COERCION_FAILED' });
if (synthetic instanceof CoercionError) {
console.assert(synthetic.code === 'COERCION_FAILED');
console.assert(synthetic.errors.length === 1);
console.log('synthetic.code:', synthetic.code);
console.log('synthetic.errors.length:', synthetic.errors.length);
const first = synthetic.errors.items[0];
if (first === undefined) {
throw new Error('expected validation error');
}
console.log('errors[0].path:', first.path);
console.log('errors[0].keyword:', first.keyword);
console.log('errors[0].message:', first.message);
}
TransformError Runtime
Thrown for. Base class for directional transform failures. Not thrown directly by the library. See DecodeError and EncodeError.
Extends. BaseError.
Adds.
| Field | Type | Notes |
|---|---|---|
direction | 'decode' | 'encode' | Which direction the transform was running when it failed |
schemaId | string | undefined | The $id of the schema whose transform failed; filled in by the library when absent |
path | string | undefined | JSON Pointer to the field being decoded/encoded, when known |
Codes.
| Constant | Value | Subclass |
|---|---|---|
TRANSFORM_ERROR_CODE.TRANSFORM_DECODE_FAILED | TRANSFORM_DECODE_FAILED | DecodeError |
TRANSFORM_ERROR_CODE.TRANSFORM_ENCODE_FAILED | TRANSFORM_ENCODE_FAILED | EncodeError |
DecodeError Runtime
Thrown for. Failure inside a decode transform function, raised by jt.instantiate() when the registered decode function throws. The original throw is preserved on cause.
Extends. TransformError → BaseError.
Constructor. new DecodeError(message: string, options?: { schemaId?, path?, cause?, retryable? }).
Fields. code === 'TRANSFORM_DECODE_FAILED', direction === 'decode', plus schemaId?, path?, cause? inherited from TransformError.
Consumer use. Custom decode handlers may throw new DecodeError('message', { path: '/field' }). The library propagates the thrown instance unchanged: message, code, path, and any other fields set by the caller are preserved. Missing schemaId context is filled in automatically.
/**
* Transform errors — Example: DecodeError, EncodeError, CoercionError
* Demonstrates: typed error handling for decode/encode failures and value.cast
*
* Three scenarios from Coreander's antiquariat:
* a. A decode transform rejects a malformed placement timestamp → DecodeError.
* b. A custom decoder throws its own DecodeError → library preserves the instance.
* c. An encode transform throws → EncodeError caught from jt.encode.
* d. value.cast on data that cannot satisfy the schema → CoercionError.
*/
import {
CoercionError,
DecodeError,
EncodeError,
Transform,
TransformError
} from '../../../src/index.js';
import {
BookSchema,
createBookstoreDocRegistry
} from '../bookstore/index.js';
// createBookstoreDocRegistry seeds a permissive copy of the bookstore — docs examples extend
// it with ad-hoc demo schemas; strict-graph checking is intentionally off here.
const jt = createBookstoreDocRegistry();
// ─── a. Decode transform rejects bad input → DecodeError ────────────────────
const StrictDateSchema = Transform.create(
{
'$id': 'https://bookstore.example/StrictDate',
'format': 'date-time',
'type': 'string'
} as const,
{
'decode': (raw: string) => {
const ms = Date.parse(raw);
if (Number.isNaN(ms)) {
throw new TypeError(`Not a valid date string: "${raw}"`);
}
return new Date(ms).toISOString();
},
'encode': (isoString: string) => {
return isoString;
}
}
);
jt.set(StrictDateSchema);
try {
jt.instantiate(StrictDateSchema, 'not-a-date');
} catch (error) {
if (error instanceof DecodeError) {
console.log('a. DecodeError caught');
console.log(' code :', error.code);
console.log(' direction :', error.direction);
console.log(' cause :', error.cause?.message);
}
}
// ─── b. Custom decoder throws DecodeError → instance preserved ───────────────
const AnnotatedDateSchema = Transform.create(
{
'$id': 'https://bookstore.example/AnnotatedDate',
'format': 'date-time',
'type': 'string'
} as const,
{
'decode': (raw: string) => {
if (!raw.startsWith('19') && !raw.startsWith('20')) {
throw new DecodeError('Year out of antiquariat range', {
'code': 'TRANSFORM_DECODE_FAILED',
'direction': 'decode',
'path': '/placedAt'
});
}
return new Date(raw).toISOString();
},
'encode': (isoString: string) => {
return isoString;
}
}
);
jt.set(AnnotatedDateSchema);
try {
jt.instantiate(AnnotatedDateSchema, '1800-01-01T00:00:00Z');
} catch (error) {
if (error instanceof DecodeError) {
console.log('b. Custom DecodeError preserved');
console.log(' message :', error.message);
console.log(' path :', error.path);
console.log(' is TransformError:', error instanceof TransformError);
}
}
// ─── c. Encode transform throws → EncodeError ───────────────────────────────
const GuardedEncodeSchema = Transform.create(
{
'$id': 'https://bookstore.example/GuardedEncode',
'format': 'date-time',
'type': 'string'
} as const,
{
'decode': (raw: string) => {
return new Date(raw).toISOString();
},
'encode': (isoString: string) => {
const date = new Date(isoString);
if (date.getFullYear() < 1900) {
throw new Error('Cannot encode dates before 1900 to wire format');
}
return isoString;
}
}
);
jt.set(GuardedEncodeSchema);
const ancientDate = '1879-01-01T00:00:00Z';
try {
jt.encode(GuardedEncodeSchema, ancientDate);
} catch (error) {
if (error instanceof EncodeError) {
console.log('c. EncodeError caught');
console.log(' code :', error.code);
console.log(' direction :', error.direction);
console.log(' cause :', error.cause?.message);
}
}
// ─── d. value.cast on uncoercible data → CoercionError ──────────────────────
// BookSchema requires 'title', 'isbn', 'authors', 'price', 'printStatus'.
// Passing an empty object fails coercion.
try {
jt.value.cast(BookSchema.$id, {});
} catch (error) {
if (error instanceof CoercionError) {
console.log('d. CoercionError caught');
console.log(' code :', error.code);
console.log(' errors.length:', error.errors.items.length);
}
}
EncodeError Runtime
Thrown for. Failure inside an encode transform function, raised by jt.encode() (and dump) when the registered encode function throws. The original throw is preserved on cause.
Extends. TransformError → BaseError.
Constructor. new EncodeError(message: string, options?: { schemaId?, path?, cause?, retryable? }).
Fields. code === 'TRANSFORM_ENCODE_FAILED', direction === 'encode', plus schemaId?, path?, cause? inherited from TransformError.
Consumer use. Custom encode handlers may throw new EncodeError('message'). The library propagates the thrown instance unchanged.
MaterializationError Runtime
Thrown for. Materialization failure - the result of materialize() (or ABox projection) failed validation.
Adds. schemaId: string and validationErrors: string[] (formatted path: message strings).
Codes.
| Constant | Value | When thrown |
|---|---|---|
MATERIALIZATION_ERROR_CODE.MATERIALIZATION_FAILED | MATERIALIZATION_FAILED | Default materialization failure. |
MATERIALIZATION_ERROR_CODE.CYCLIC_DATA | CYCLIC_DATA | Circular reference detected during ABox projection (toQuads). Thrown by Projection when a data object contains a cycle that would loop indefinitely during RDF quad emission. |
MATERIALIZATION_ERROR_CODE.INVALID_IRI_VALUE | INVALID_IRI_VALUE | Thrown when a value that must be an IRI is not a valid IRI. |
MATERIALIZATION_ERROR_CODE.NON_FINITE_NUMBER | NON_FINITE_NUMBER | Thrown when a numeric value is Infinity or NaN, which cannot be represented in RDF. |
MATERIALIZATION_ERROR_CODE.MISSING_GRAPH_IRI | MISSING_GRAPH_IRI | Thrown when a required named-graph IRI is absent during ABox projection. |
/**
* MaterializationError — materialization failed validation.
*
* `materialize()` without `enablePartial` fails when the canonical
* OrderSchema's required fields have no declared defaults — the
* Bastian-orders fixture's required `id`, `customerId`, `items`,
* `total`, `placedAt`, and `shippingAddress` cannot be synthesised
* from zero-values alone.
*/
import { MaterializationError } from '../../../src/index.js';
import {
bookstoreEntities, OrderSchema
} from '../bookstore/index.js';
try {
bookstoreEntities.materialize(OrderSchema, {});
} catch (error) {
if (error instanceof MaterializationError) {
console.assert(error.code === 'MATERIALIZATION_FAILED');
console.assert(error.schemaId === OrderSchema.$id);
console.assert(Array.isArray(error.validationErrors));
console.log('error.code:', error.code);
console.log('error.schemaId:', error.schemaId);
console.log('error.validationErrors:', error.validationErrors);
}
}
Inspecting the cause chain
Every error supports flatten(), which walks the cause chain and returns a root-first array of plain objects suitable for structured logging.
/**
* Inspecting the cause chain via `flatten()`.
*
* `InstantiationError.flatten()` walks the cause chain and appends
* every item in the embedded `errors` collection. A single call
* surfaces both the wrapper and each underlying validation issue.
*/
import { InstantiationError } from '../../../src/index.js';
import {
bookstoreEntities, ReviewSchema
} from '../bookstore/index.js';
try {
bookstoreEntities.instantiate(ReviewSchema.$id, {
'body': 'no',
'rating': 12
});
} catch (error) {
if (error instanceof InstantiationError) {
const entries = error.flatten();
console.assert(entries.length > 0);
console.log('flatten() entry count:', entries.length);
for (const entry of entries) {
console.log(` code=${entry.code} retryable=${entry.retryable} message=${entry.message}`);
}
}
}
InstantiationError.flatten() and CoercionError.flatten() additionally append every item in their errors collection, so a single call surfaces both the wrapper and each underlying validation issue.
Related
- ValidationErrors overview - the collection embedded inside
InstantiationErrorandCoercionError - ValidationErrors views -
aggregate()andreport()projections
See also
- Bookstore domain - schema definitions used in catch examples