How it works
The attempt.repairs field
Inside your RunFn, attempt.repairs is an array of plain { role, content } JSON objects. Most chat-style providers accept this shape directly or with a small mapping step.
attempt.repairs looks like this:
role: "user" — they carry the failure text from your rule’s check (or the schema/parse error) so the model sees exactly what to fix on the next attempt.
repairs is empty. On subsequent attempts, it contains the failure messages from the previous attempt — one entry per rule or schema violation that fired.
Retry configuration
By default, Boundary retries up to 3 attempts with no delay between them.| Strategy | Delay pattern |
|---|---|
"none" | No delay between retries (default) |
"linear" | baseMs * attemptNumber |
"exponential" | baseMs * 2^attemptNumber |
Failure categories
Every failed attempt is classified into one of 8 categories. Each category has a default repair strategy:| Category | Meaning | Default repair |
|---|---|---|
EMPTY_RESPONSE | Model returned nothing | Re-prompt with schema instructions |
REFUSAL | Model refused the task | Re-prompt emphasizing the task is valid |
NO_JSON | Response contained no JSON | Ask for JSON output specifically |
TRUNCATED | JSON was cut off | Ask for complete, shorter response |
PARSE_ERROR | Malformed JSON | Send the parse error details |
VALIDATION_ERROR | Valid JSON, failed schema | Send schema violations |
RULE_ERROR | Passed schema, failed rules | Send rule violations (your domain logic) |
RUN_ERROR | Your RunFn threw an error | No retry by default |
Custom repair overrides
You can override or disable repair for specific categories:false stops retrying for that failure type. Providing a function lets you craft custom repair messages.
When repair fails
IfmaxAttempts is exhausted without a valid output, the contract returns { ok: false, error }. The full per-attempt history is on error.attempts — including the failure category, the raw model output, and the typed ruleIssues on RULE_ERROR attempts.
maxAttempts regularly, that’s a signal: the rule may be too strict, the prompt may be missing context, or the model may be wrong for the job. Local logs can show this while you build; the hosted SDK can aggregate it across production traffic. See Results for handling patterns.
Next steps
Results
The ContractResult type
Guarantees
What Boundary promises