Reliability
Reliability in Shiro means knowing which boundary failed. Provider timeouts, tool hangs,
approval rejections, and validation errors need different responses. One generic catch
makes unsafe retries likely.
Failure boundaries#
| Boundary | Typical signal | Safe default |
|---|---|---|
| Provider | timeout / HTTP / rate limit | Retry with backoff |
| Tool (read) | timeout / 5xx | Retry if idempotent |
| Tool (write) | partial failure | Retry only with idempotency keys |
| Approval | rejected | Surface to user; do not auto-retry |
| Output validation | parse failure | Repair policy or fail the run |
| Handoff | unresolved agent | Fix registration; do not loop |
See Error Handling.
Timeouts#
Bound the slow parts you do not control: model latency and external tools. Prefer configuring timeouts at the execution / tool edge rather than hoping the platform kills the process.
Keep mutating tools short or explicitly async with a job id you can poll.
Retries#
Retry reads liberally; retry writes only when the operation is idempotent.
const issueRefund = tool({
name: "issueRefund",
requiresApproval: true,
parameters: z.object({
orderId: z.string(),
amount: z.number(),
idempotencyKey: z.string(),
}),
execute: async (input) => refunds.issue(input),
});Trace failed runs#
A retry without a trace is a guess. Export failures with Tracing and inspect them in Studio before changing prompts.
Best practices#
- Idempotency keys on refunds, deletes, deploys
- Explicit timeouts before production traffic
- Treat approval rejection as a valid outcome
- Store enough tool I/O to reproduce bugs
Common mistakes#
- Retrying payments because the provider timed out afterward
- Catching all errors at the HTTP layer and returning 500
- Hiding provider errors inside tool return strings