Skip to content

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>
      )}
    </>
  );
}
Northwind Invoicing
It fails on purpose.

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.

app.northwind.example/invoices

Northwind · Your work

Model

Send invoice #1043

Northwind BV · Belgium · EUR 4,820.00

Not sent

All your app caught

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

Import failed

All your app caught

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.

Scritch: recover blocked users