Errors and Remedies
Blueprint errors are declaration vocabulary, not strings invented inside route handlers. A module owns a catalog of errors and custom remedies. Actions and rules point into that catalog with typed references. Runtime packages bind typed vyuh_errors parsers without putting functions into the portable Blueprint.
Identity contract
Every error definition has four distinct identities:
| Property | Purpose |
|---|---|
schemaType | Semantic discriminator: vyuh.blueprint.error. |
name | Stable declaration name within the module. |
title | Human-facing display string. |
code | Globally qualified wire and registry identity. |
description is the safe default message. kind, severity, httpStatus, retryable, and recoverable drive protocol and UI behavior. Typed context fields describe the safe details allowed to cross the protocol boundary.
const equipmentUnavailable = ErrorDefinition(
name: 'equipment_unavailable',
title: 'Equipment Unavailable',
code: 'ops.equipment.unavailable',
description: 'The equipment is temporarily unavailable.',
kind: BlueprintErrorKind.conflict,
httpStatus: 409,
retryable: true,
context: [
ErrorContextField(
name: 'equipment_id',
title: 'Equipment',
description: 'The equipment that rejected the operation.',
valueType: ErrorContextValueType.identifier,
required: true,
),
],
remedies: [RemedyRef('vyuh.retry')],
);Declare the definition on its owning module and reference it from actions and rules:
Module(
name: 'ops',
title: 'Operations',
schema: 'ops',
version: '1.0.0',
errors: [equipmentUnavailable],
entities: [
Entity(
name: 'equipment',
title: 'Equipment',
tableName: 'equipment',
facets: [
IdentityFacet(
actions: [
Action(
name: 'activate',
title: 'Activate',
raises: [ErrorRef('ops.equipment.unavailable')],
rules: [
Rule(
id: 'equipment_available',
kind: RuleKind.policy,
errorRef: ErrorRef('ops.equipment.unavailable'),
),
],
),
],
),
],
),
],
)The validator rejects duplicate error codes, duplicate remedy IDs, unknown ErrorRefs, unknown custom RemedyRefs, duplicate context fields, and missing display identity.
vyuh_errors registry integration
At server bootstrap, BlueprintPlugin converts every ErrorDefinition into an ErrorCodeEntry and registers it with vyuh_errors. A BlueprintTarget.errorDescriptors adds domain parsers for typed StructuredException subclasses:
BlueprintTarget(
blueprint: opsBlueprint,
errorDescriptors: [opsErrors],
)This separation is intentional:
- Blueprint owns portable identity, display text, status, context schema, and remedy references.
- Runtime packages own Dart parser/factory functions.
- Server and client register the same domain descriptor, so deserialization is symmetric.
Runtime exception families
StructuredException is the serialization and registry base. Blueprint uses two explicit runtime families above it:
BlueprintProtocolExceptionowns Blueprint validation, compilation, protocol, storage-adapter, and runtime-integrity failures.DomainExceptionowns application and business failures declared by domain modules.
Concrete Blueprint exceptions must extend BlueprintProtocolException; concrete domain exceptions must extend DomainException. The client throws those exception families directly. HTTP and Dio remain adapter details; status, retryability, correlation IDs, and the safe response envelope are available separately as RemoteFailureMetadata. Callers therefore choose recovery and presentation by exception type rather than parsing a code prefix or depending on a transport-specific exception.
Protocol and stack-trace contract
The server catches (error, stackTrace) and logs the complete exception and stack on the server with a generated error_id. A caller receives the safe structured envelope:
{
"error": {
"code": "ops.equipment.unavailable",
"kind": "conflict",
"layer": "domain",
"message": "The equipment is temporarily unavailable.",
"status": 409,
"error_id": "bp-...",
"trace_id": "optional-request-trace",
"retryable": true,
"recoverable": true,
"details": { "equipment_id": "eq-1" },
"remedies": [{ "id": "vyuh.retry", "label": "Retry" }],
"structured_error": {
"code": "ops.equipment.unavailable",
"context": { "equipment_id": "eq-1" }
}
}
}Production responses never include raw stack traces, SQL text, credentials, or adapter internals. Operators correlate error_id or trace_id with server logs. The Blueprint HTTP client preserves the protocol envelope and dispatches structured_error through the registered vyuh_errors domain parser, so generic error UI can render typed details and remedies without matching messages.
Compatibility
Inline ActionError values remain readable for existing declarations. New declarations should use module ErrorDefinitions plus Action.raises and Rule.errorRef; this makes one catalog authoritative across docs, Studio, server status mapping, serialization, and client remedies.