Your app already knows why it failed. Your user doesn't.
Every product ships “Something went wrong. Please try again.” This finds the real cause, asks the one question only the user can answer, fixes it with their approval, and proves it worked before saying so.
Five failures a real product ships. Pick one and press the button.
401 Unauthorized · token expired 40 minutes ago
What most products write
async function onSave(draft: Draft) {
try {
await api.saveDraft(draft);
} catch (err) {
// The 401 knew the token expired. This throws that away.
toast.error("Something went wrong. Please try again.");
}
}The same handler, wired up
import { useRecoverableTask, useGuidance } from "scritch-react";
function SaveButton({ draft }: { draft: Draft }) {
const { incident, run } = useRecoverableTask({ code: "draft.save.rejected" });
const guidance = useGuidance(incident);
return (
<>
<button onClick={() => run(() => api.saveDraft(draft))}>Save</button>
{/* Your toast, your styling. The hook only returns text. */}
{guidance.message && (
<Toast tone={guidance.userCanFix ? "action" : "info"}>
{guidance.message}
</Toast>
)}
</>
);
}A real run, not a script
Everything above is scripted. This is a working install in a pretend invoicing app, on the same agent your workspace would run. It picks up the failure, reads what the app never surfaced, asks you the one thing only you can answer, and confirms you can carry on, while you wait rather than after you have gone. It needs no error monitoring to do this. Connect yours and it reads that too.
Northwind · Your work
Send invoice #1043
Northwind BV · Belgium · EUR 4,820.00
All your app caught
Something went wrong. Please try again.
POST /api/invoices/1043
→ 422 Unprocessable Entity
{ "error": "validation_failed" }What the agent does with it
You have tried four times. Nothing on the form is marked as wrong.
Import contacts.csv
240 rows · uploaded 2 minutes ago
All your app caught
Import failed. Please check your file and try again.
POST /api/contacts/import
→ 207 Multi-Status
{ "imported": 198, "failed": 42, "rows": null }What the agent does with it
42 of 240 rows were not imported. The app does not say which, or why.
How it works
- It reads, first
- Read-only tools you register run unattended: the server's real response, the rules your form never surfaced, whatever your monitoring recorded.
- It asks only what it cannot find
- Ambiguous dates in a CSV could be day-first or month-first, and no tool knows which, so it asks once, in plain words.
- It changes nothing without you
- Any tool that writes stops for approval, showing the exact effect and what can be undone.
- It proves the fix
- Recovery is declared by a postcondition check, never by the model saying so.
- It hands off what it cannot fix
- Repair needs a pack you wrote for that failure. For anything else it still explains the failure in plain words and gives support everything it found, so the person never has to repeat themselves.
The questions your security reviewer will ask
Who this is for
B2B software where a blocked user costs a support ticket: integrations, onboarding, imports, billing, anything with a form a server can refuse.
Not an error monitor. If you want exceptions grouped and alerted, you already have Sentry, and this reads from it rather than replacing it.
Where this is
Early. Nothing here has run in production yet, and the examples above are scripted. The one live thing on this page is the working install further down. The client SDK is React today.
See it on one of your own failures
A workspace takes a minute. Point it at one endpoint that refuses people and watch what it says.