# Failure — what went wrong the last time an action ran

> When an action fails — a security denial, a constraint violation, a server that refused — the platform records why, and `Failure` is how a page says so. `Failure.Any` is the flag a surface binds its visibility to, `Failure.Message` carries the words of whatever refused it, and `Failure.Dismiss` clears it. It is a read surface bound into every render scope, needs no `using`, and has no appearance of its own: whether the message becomes a banner, a toast or an inline note is yours to draw.

<!-- id: ui-failure · area: ui · stability: stable · html: https://osysharp.com/reference/ui/failure/ -->

## Summary        {#summary}
An action can fail. A security rule refuses the write, a constraint rejects the row, the server is unreachable — and
without somewhere to put that, the person who pressed the button sees a button that did nothing. `Failure` is where
the platform puts it:

```osy title="the whole surface — a banner that appears the moment an action fails" test app=ui-failure
component SaveBar() {
  render {
    if (Failure.Any) { Alert(Failure.Message, tone: Tone.Danger); }
  }
}
```

`Failure` is bound into **every render scope** and is part of the core baseline, so it needs no `using` and nothing is
declared to switch it on. It is filled by the runtime whenever an action fails, and it holds the **most recent**
failure until something clears it.

## Signature      {#signature}
```osy syntax
Failure.Any             // bool   — did the last action fail, and has nobody dismissed it yet?
Failure.Message         // string — what went wrong, in the words of whatever refused it
Failure.CorrelationId   // string — the id to paste into `osy logs --correlation <id>`
Failure.Action          // string — the action that failed, as `ui:<Component>.<Action>`
Failure.Dismiss         // Action — clear it; `Failure.Any` goes false
```

## Description    {#description}

### What fills it, and in whose words   {#what-fills-it}
Any action that fails fills it — an `onPress`, an `onChange`, a lifecycle hook, anything the page RUNS. The message is
taken from **whatever actually refused the call**, in that thing's own words, because those are the words that say
something useful:

| what failed | what `Failure.Message` reads |
|---|---|
| a security rule | the rule's own sentence — *"Create of 'Invoice' denied — no `create` rule is active for this caller…"* |
| a constraint or an invariant | the author's own `message` from the declaration |
| a `throw` in your function | the exception's message |
| the transport, and only then | the HTTP wording — *"POST /fn/save failed: 503"* |

The transport wording is the **last** resort rather than the first. A denial that reached the user as `403` would be
technically true and useless; the rule that refused it already wrote a sentence meant for a person, and that is the one
that surfaces.

### Showing it once, not once per frame   {#once-per-cause}
`Failure.Any` is a flag, not a stream. It is raised **once per cause** and stays raised until dismissed or until the
next failure replaces it — so a surface bound to it appears once and stays, rather than four copies arriving a second
apart. Binding a banner's visibility straight to `Failure.Any` is therefore the whole of the work.

### Clearing it, so a banner is not permanent   {#dismiss}
`Failure.Dismiss` is a **verb the platform supplies**, not one you write: the value lives in the client runtime, so
only the runtime can clear it. It is declared as an `Action`, which means it goes wherever an action is wanted —
`onPress: Failure.Dismiss` — and needs no wrapper:

```osy title="a dismissible banner — the close button IS the platform's verb" test app=ui-failure
component DismissibleFailure() {
  render {
    if (Failure.Any) {
      Row(gap: 8, align: Align.Center) {
        Alert(Failure.Message, tone: Tone.Danger);
        Button("Dismiss", onPress: Failure.Dismiss, tone: Tone.Ghost);
      }
    }
  }
}
```

Without a dismiss the banner persists until the next navigation, which is a reasonable default and a poor experience
on a page the user then goes on working in.

### Giving the user something to report   {#correlation-id}
`Failure.CorrelationId` is the id that ties this failure to its server-side log line. Surfacing it rather than only
logging it is worth the pixels: a failure a user cannot report is most of the way to a failure nobody can fix, and
`osy logs --correlation <id>` turns a screenshot into a diagnosis.

```osy title="the message a person reads, and the id they can quote" test app=ui-failure
component FailureNote() {
  render {
    if (Failure.Any) {
      Stack(gap: 4) {
        Text(Failure.Message, fontWeight: "600");
        Text("Reference: " + Failure.CorrelationId, fontSize: "0.85rem", opacity: "0.7");
      }
    }
  }
}
```

`Failure.Action` names the action that failed, as `ui:<Component>.<Action>` — for a surface that wants to say which
verb it was, or to show a failure only for one particular action.

### Three failure surfaces, and which one you want   {#not-the-others}
Three things can go wrong and they are deliberately separate, because replacing the whole screen for a failed button
would be a lie about what broke:

| what broke | the surface | what the user sees |
|---|---|---|
| an **action** on a page that rendered fine | **`Failure`** — this page | the page still works; a message says the action did not |
| a **query** a region reads | [A failing query](https://osysharp.com/reference/ui/query-failure/) | that region shows the failure in place; the rest of the page is fine |
| the **page itself** failed to load | `app.Ui.ErrorSurface` ([UI surfaces (app.Ui)](https://osysharp.com/reference/config/ui/)) | the page is replaced |

Field-level rejections are different again: a `ValidationException` carries per-field violations you place beside the
offending field, which is [Validation](https://osysharp.com/reference/ui/validation/). `Failure` is the catch-all for the action as a whole.

### What is drawn when you draw nothing   {#zero-policy}
A page that reads `Failure` nowhere is not left silent: the platform shows a plain notice at the foot of the screen —
the message, the correlation id and a **Dismiss** — so a refused action never looks like a button that did nothing.
It is plain HTML with `osy-` classes (`.osy-action-failed`, `-message`, `-correlation`, `-dismiss`), restyled in the
app's CSS. While it is up the page gets room at its foot as tall as it, and the row of the control that was pressed is
scrolled clear of it, so it never covers what it is about.

**Reading `Failure` anywhere takes the surface over**: the platform's notice stands down for the whole page, and
whether the failure becomes a banner, a toast, a line under the form or nothing at all is the app's to decide.

A save that lost a race on a `[ConcurrencyCheck]` row gets a notice of its own — what you wrote, what the row holds
now, **Keep theirs** and **Apply mine**: [The conflict notice — when a page's save meets somebody else's change](https://osysharp.com/reference/ui/conflict-notice/).

## See also       {#see-also}
- [The conflict notice — when a page's save meets somebody else's change](https://osysharp.com/reference/ui/conflict-notice/) — the notice for a save that met somebody else's change: both values, and the two ways on.
- [A failing query](https://osysharp.com/reference/ui/query-failure/) — the sibling surface for a failed READ, shown in the region that reads it.
- [UI surfaces (app.Ui)](https://osysharp.com/reference/config/ui/) — `app.Ui.ErrorSurface`, for a page that failed to load rather than an action that failed.
- [Validation](https://osysharp.com/reference/ui/validation/) — per-field rejections, placed beside the field that was refused.
- [Pending](https://osysharp.com/reference/ui/pending/) — the other half of action feedback: what the user sees while it is still running.
- [Connection](https://osysharp.com/reference/ui/connection/) — the connection-loss overlay, for when the server is gone rather than refusing.
