# Failure

Our fault or a dependency’s. The caller sees a generic message; the details are logged. 5xx.

Pattern: `failure.*` (this page) · Raised with: [`fail()`](https://www.prongs.dev/docs/profiles/errors.md) · Intents: [3](#intents) · Statuses: `500` `502` `503`

## Intents

| Intent | Status | Use it for |
|---|---|---|
| [`failure.internal`](https://www.prongs.dev/docs/intents/failure/internal.md) | `500` | Our fault: a bug, a failed database call. The caller gets a generic message; the details go to the log |
| [`failure.upstream`](https://www.prongs.dev/docs/intents/failure/upstream.md) | `502` | A service we depend on failed or timed out: a feed, an email provider, an API |
| [`failure.unavailable`](https://www.prongs.dev/docs/intents/failure/unavailable.md) | `503` | Temporarily unavailable: maintenance, missing configuration, overload |

## Raising one

Name what went wrong. The intent sets the status, the message the caller sees, and any log it writes.

```ts
// Our fault: a bug, a failed database call. The caller gets a generic message; the details go to the log
return fail('failure.internal', undefined, { cause: error });

// A service we depend on failed or timed out: a feed, an email provider, an API
return fail('failure.upstream', 'Feed host timed out.', { cause: error });

// Temporarily unavailable: maintenance, missing configuration, overload
return fail('failure.unavailable', 'SUPABASE_SERVICE_ROLE_KEY is not set.');
```

The caller gets (failure.internal)

```bash
HTTP 500

{
  "ok": false,
  "error": "Something went wrong. Try again.",
  "code": "failure.internal"
}
```

And it writes incident.failed (failure.internal)

```ts
{
  "intent": "incident.failed",
  "level": "error",
  "alert": true,
  "message": "Something went wrong. Try again.",
  "data": {
    "intent": "failure.internal",
    "cause": {
      "name": "PostgresError",
      "message": "connection terminated unexpectedly",
      "stack": "PostgresError: connection terminated…"
    }
  },
  "time": "2026-10-09T12:00:00.000Z"
}
```

## Is it a failure error?

Find the row that describes your case, and use the domain on the right.

| If the problem is | Use |
|---|---|
| The caller’s request was wrong: unreadable, incomplete, or a value that breaks a rule. 400 or 422. | [`request.*`](https://www.prongs.dev/docs/intents/request.md) |
| No valid credentials: nobody signed in, an expired session, a bad token or secret. 401. | [`auth.*`](https://www.prongs.dev/docs/intents/auth.md) |
| Valid credentials, but not allowed to do this. 403. | [`permission.*`](https://www.prongs.dev/docs/intents/permission.md) |
| The thing asked for is not there, as far as the caller may know. 404. | [`record.*`](https://www.prongs.dev/docs/intents/record.md) |
| The request clashes with the current state: it already exists, or it is not possible yet. 409. | [`conflict.*`](https://www.prongs.dev/docs/intents/conflict.md) |
| Too many requests, or an allowance used up. 429. | [`limit.*`](https://www.prongs.dev/docs/intents/limit.md) |
| Our fault or a dependency’s. The caller sees a generic message; the details are logged. 5xx. | `failure.*` (this page) |

## For registry authors

**Starter intents.** These are the intents `prongs add errors` puts in your registry, raised with [`fail()`](https://www.prongs.dev/docs/profiles/errors.md). Change the messages to suit your app, and the statuses through the [`status.*`](https://www.prongs.dev/docs/intents/errors.md#status) intents they are built from.
