Skip to main content
Every contract call returns a ContractResult<T> — a discriminated union on the ok field. Boundary never throws. You always get a structured result.

Success: result.ok === true

When ok is true, data is fully typed from your Zod schema. It has passed schema validation and every rule. Safe to use directly in your application.

Failure: result.ok === false

When ok is false, no data is returned to your application. The error object contains the full history of every attempt.

AttemptDetail

Each failed attempt records:
ruleIssues is the typed companion to issues — same content, but with the rule’s name and fields attached so you can route, count, or trigger logic per-rule on the receiving side.

Pattern matching

Basic

Early return

Logging attempts

Handling permanent failure

When maxAttempts is exhausted without an accepted output, you decide what happens next. The full attempt history is on result.error.attempts — use it to choose between retry-with-different-prompt, escalate-to-human, or fail-fast.
The structured per-rule data on ruleIssues makes per-rule alerting cheap. Pair it with a metrics sink and you get a “which rule is degrading?” view with no extra plumbing.

Failure categories

Every failed attempt is classified: The category tells you where the failure happened in the pipeline. VALIDATION_ERROR means the structure was wrong. RULE_ERROR means the structure was right but the values were wrong — this is the gap Boundary exists to close.

Why not exceptions?

ContractResult<T> is the error handling path. No try/catch needed. If you want to throw:
But the result type gives you more: the full attempt history, failure categories, and specific violations. Exceptions throw that away.

Next steps

Guarantees

What Boundary promises when result.ok is true

When to Use

Where Boundary fits — and where it doesn’t

Observability

Use local development logging to inspect individual rejected results while building. Use production observability when you need acceptance rates, top failing rules, and recent rejected runs in the dashboard.