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

if (result.ok) {
  result.data       // T — typed, validated, all rules passed
  result.attempts   // number — total attempts made (minimum 1)
  result.raw        // string — the raw LLM output that was accepted
  result.durationMS // number — total wall-clock time including retries
}
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

if (!result.ok) {
  result.error.message   // string — human-readable summary
  result.error.attempts  // AttemptDetail[] — one per failed attempt
}
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:
type AttemptDetail = {
  raw: string           // the raw LLM output
  cleaned: unknown      // the parsed/cleaned output
  issues: string[]      // list of violations (schema or rule)
  category: FailureCategory  // what type of failure
}

Pattern matching

Basic

const result = await contract.accept(run);

if (result.ok) {
  await saveToDatabase(result.data);
} else {
  console.error("Contract failed:", result.error.message);
}

Early return

const result = await contract.accept(run);
if (!result.ok) {
  return res.status(422).json({ error: result.error.message });
}
// result.data is typed T from here
await processLead(result.data);

Logging attempts

if (!result.ok) {
  for (const attempt of result.error.attempts) {
    console.log(`Attempt failed [${attempt.category}]:`, attempt.issues);
  }
}

Failure categories

Every failed attempt is classified:
CategoryMeaning
EMPTY_RESPONSEModel returned nothing
REFUSALModel refused the task
NO_JSONResponse contained no JSON
TRUNCATEDJSON was cut off or incomplete
PARSE_ERRORMalformed JSON
VALIDATION_ERRORValid JSON but failed Zod schema
INVARIANT_ERRORPassed schema but failed your rules
RUN_ERRORYour RunFn threw an error
The category tells you where the failure happened in the pipeline. VALIDATION_ERROR means the structure was wrong. INVARIANT_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:
const result = await contract.accept(run);
if (!result.ok) throw new Error(result.error.message);
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