Guided Failure Recovery
When a MicroSkill can’t complete a request, Squillify doesn’t just hand your agent a dead-end error string. It returns a structured, machine-readable explanation of whythe call failed and — when the problem is repairable — a ranked list of currently accessible skills your agent can call next to fix the input and try again, each still subject to the normal checks on that follow-up call. Guided Failure Recovery (GFR) turns a failure into a next step.
The problem it solves
A traditional API answers a bad request with something like error: “Row at line 2 has 4 columns, expected 3”. For a human that’s a mild annoyance. For an autonomous AI agent it’s a dead end: the agent has to guesswhat went wrong, guess whether anything can fix it, and guess which tool to reach for — often burning tokens and retries on approaches that were never going to work.
GFR removes the guessing. Every recovery-aware failure comes back as a typed signal your agent can branch on programmatically, paired with concrete, already-accessible skills that repair the path. The agent stays in control — Squillify only advises — but it now has everything it needs to make a good decision on the first try.
What you get back
A recoverable failure carries two structured objects alongside the usual error. You route on the fields, never on the prose message.
The failure envelope — why it failed
| Field | What it tells you |
|---|---|
| category | A stable, coarse taxonomy of the failure — e.g. VALIDATION_ERROR, PRECONDITION_FAILED, POLICY_DENIED, LIMIT_EXCEEDED, DEPENDENCY_UNAVAILABLE. |
| code | A stable, specific reason code such as CSV_MALFORMED or JSON_MALFORMED. Safe to branch on; a code change is treated as a contract change. |
| recoverable | true when a repair path may exist. Only recoverable failures trigger a recovery lookup. |
| retryable | true when the exact same call might succeed if you simply retry it (a transient blip). Different from recoverable. |
| recovery_intent | The kind of repair needed (e.g. format_inspection, json_repair) — a stable token describing the fix, not a skill name. |
| message / details | Human-readable context only. Never parse the message; details carry bounded, safe metadata (never your payload). |
The recovery block — what you can do next
When a recoverable failure has a known repair intent, Squillify attaches an advisory recovery block with a ranked list of options:
| Field | What it tells you |
|---|---|
| available | true when at least one option you can actually access was found. |
| options[].skill_name / skill_version | The exact skill to call to perform the repair. At response time it has passed your current access scope; the follow-up call still goes through normal authorization, quota, and policy checks. |
| options[].purpose | A plain, agent-facing sentence describing what running this skill will do for you before you retry. |
| options[].then_retry | An advisory hint naming the original skill to re-run once the repair succeeds. Squillify never runs it for you. |
How Squillify finds your options
When a failure is recoverable, the runtime asks OSRE— Squillify’s skill resolution engine, the same engine behind Dynamic Skill Resolution — for existing skills that explicitly declarethey can satisfy the repair intent. The lookup is scoped to the caller’s current access and policy context, and is deliberately trustworthy:
- Declared, not guessed. A skill can only be offered as a fix if it declares a compatible recovery capability. Squillify never suggests a skill just because it sounds similar.
- Scoped to you.Options are intersected with your API key’s current access scope. The follow-up call still goes through normal authorization, quota, and policy checks.
- Deterministic within context. For the same catalog version, access scope, policy state, and failure context, the same recovery options are returned in the same order.
- Best-effort enrichment. Recovery resolution must never mask the original failure. If it is unavailable or exceeds its budget, Squillify returns the original failure without recovery options.
How your agent uses it
Here is a real recoverable failure from data.csv_normalize, trimmed to the parts that matter:
{
"error": { "code": "CSV_MALFORMED", "message": "...", "request_id": "..." },
"failure": {
"category": "VALIDATION_ERROR",
"code": "CSV_MALFORMED",
"recoverable": true,
"retryable": false,
"recovery_intent": { "intent": "format_inspection" }
},
"recovery": {
"available": true,
"options": [
{
"rank": 1,
"skill_name": "schema.infer_csv_schema",
"skill_version": "1.0.0",
"purpose": "Inspect the CSV structure, detect column counts, and identify malformed rows before retrying the original CSV normalization.",
"then_retry": { "skill_name": "data.csv_normalize", "advisory": true }
}
]
}
}A robust recovery loop looks like this:
- On any failure, branch on
failure.categoryandfailure.code— the stable fields — not on the human message. - If
failure.recoverableandrecovery.available, call the first option’sskill_nameto repair the input, then re-run the original skill named inthen_retry. - If the failure is
retryablebut not recoverable, just retry the same call with backoff. - If
recovery.availableisfalse, don’t try to infer why — treat it as “no guided path right now” and fall back to your own handling.
Over MCP and REST
GFR is delivered the same way regardless of how you connect, and it is fully backward compatible — older clients keep working unchanged.
- MCP.The tool result keeps its existing human-readable text item and, when there’s a typed failure, appends a second JSON content item under
squillify_failurecarrying thefailureandrecoveryblocks. The recovery guidance is also baked into the tool description, so an agent connecting over MCP is told to prefer listed skills and never invent recovery skills. - REST (
POST /v1/execute). The failure body gains top-levelfailureandrecoveryobjects plus compacterror.recoverable/error.recovery_availableflags for lean clients. Advisory response headers mirror those flags for proxies, but the JSON body is always authoritative.
hasRecoveryOptions(), recoveryOptions(), and bestRecoveryOption(), so most agents do not need to parse raw JSON.Recovery, not fallback
This is the most important thing to understand about how GFR behaves. A recovery option only ever repairs your input so the original skill can be retried toward the same goal— it prepares, normalizes, sanitizes, decomposes, or enriches. It will never quietly swap in a different skill that reaches the goal another way.
The test is simple: after the suggested skill runs, can the original skill be retried with a better chance of success?If not, it isn’t offered. That keeps your agent’s intent intact and avoids silent goal substitution.
What GFR does not do
Setting expectations clearly is part of trusting the feature. GFR deliberately stays out of your agent’s way:
- It does not execute recovery automatically.
- It does not reserve quota for suggested follow-up calls.
- It does not guarantee that a suggested skill will still be available later.
- It does not invent missing skills.
- It does not replace your agent’s planning logic.
- It does not offer fallback routes that change the original goal.
Guarantees you can rely on
- Advisory, never orchestrating. Squillify resolves and suggests options; it never executes them, reserves no quota for them, and the original failure is always the canonical result.
- Caller-scoped.You’re only ever shown skills you can already access.
- No information leaks.When no option is available, Squillify says exactly that — it never reveals why (whether a skill doesn’t exist, isn’t accessible to you, or was filtered by policy).
- Policy-safe. For policy-denied failures, recovery is deny-by-default: a fix is only offered when an explicit rule allows it, so recovery can never hand you a workaround that defeats a policy.
- Billing-invariant. Turning recovery on does not change the cost of the failed call. Recovery resolution emits no billable event, and the same failure costs the same with recovery enabled or disabled. If your agent chooses to call a suggested recovery skill, that follow-up execution is billed normally.
When there’s no option yet
Sometimes a failure is genuinely recoverable in principle, but no skill in the catalog yet provides the needed repair — for example, a JSON_MALFORMED failure whose json_repairfix doesn’t exist as a skill today. Squillify records this as a catalog gap: a real product signal that helps us prioritize which capabilities to build next. Your agent simply sees recovery.available: false and handles it like any other no-guided-path case.
Why it matters
- Fewer wasted attempts. Agents stop guessing and go straight to a repair that is known to help.
- Higher end-to-end success.Complex, multi-step problems become navigable: inspect → repair → retry, guided by the platform.
- Deterministic and safe. Reproducible within a given catalog and access context, scoped to your access, with no billing surprises and no policy escapes.
- Zero migration cost.It’s additive over both REST and MCP; existing integrations keep working, and you adopt it when you’re ready.