# throw

> Raises a fault. It ends the function immediately, and the rows the function wrote are discarded rather than half-written. An exception is a class: throw one the platform declares, or declare your own the way C# does — `class CardDeclinedException : PaymentException { }` — and a `catch` for it or any ancestor takes it. Exceptions are usable as ordinary types too, so a variable, a field or a parameter can hold one. CATCHING one is different: the refused row stays staged, and the catch has to say what happens to it.

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

## Summary        {#summary}
`throw` raises a fault. The function stops where it stands, and **the rows it wrote are discarded** — a function that
throws leaves nothing half-written behind it.

You throw an exception **class** — one the platform declares, or [[#declare-your-own|one you declare]]:

```osy title="refuse, and say why" test app=function-throw
entity Order {
  [Required, Unique, MaxLength(20)] string Code;
  decimal Total;

  // Every entity states who may touch it — with no `security { }` block it is denied to everyone.
  // A real app scopes these grants to a user; an example still has to declare them, because an example
  // that could not actually run is not an example. See the security guide.
  security { allow create, read, update when IsAuthenticated || IsAnonymous; }
}

void Refund(string code, decimal amount) {
  var order = Order.SingleOrDefault(o => o.Code == code);
  if (order == null) { throw new NotFoundException($"no order '{code}'"); }
  if (amount > order.Total) { throw new ValidationException("a refund cannot exceed the order total"); }

  order.Total = order.Total - amount;
}
```

## Signature      {#signature}
```osy syntax
throw new Exception("<message>");                  // the base type — the catch-all
throw new NotFoundException("<message>");          // asked for a thing that is not there
throw new ValidationException("<message>");        // the input or the resulting row is not acceptable
throw new ConflictException("<message>");          // it clashes with the current state of the data
throw new ExternalServiceException("<message>");   // a service outside the app refused the request
throw new OAuthConnectionFailedException("<msg>"); // an external authorization handshake failed

class <Name>Exception : <an exception type> { }    // your own — derive from Exception or any type above

var e = new ConflictException("<message>");         // an exception as a VALUE — return it, store it, pass it…
throw e;                                           // …and throw it later, as itself
```

## Description    {#description}

### Which types are there?   {#the-types}
These are the ones the platform declares — and the ones it raises on your behalf. Naming a type that is not an
exception is a compile error that says how to declare one and lists these **with what raises each**, so you cannot
misspell your way into a silent catch-all:

| Type | What it means | Also raised for you by |
|---|---|---|
| `Exception` | the base. Every other type is one, so `catch (Exception e)` catches everything | — |
| `NotFoundException` | you asked for something that does not exist | — |
| `ValidationException` | the input, or the row you are about to write, is not acceptable | a broken [[entity-constraints\|constraint]] or [[entity-invariants\|invariant]] — including a duplicate under `[Unique]` |
| `ConflictException` | it cannot be done given the current state of the data | two writers colliding — see [ConcurrencyCheck](https://osysharp.com/reference/entity/concurrency-check/) |
| `ExternalServiceException` | a service outside the app answered a request with a failure — a bucket that refused a write, an API that said no | — throw it (or a type you derive from it) from code that calls one |
| `OAuthConnectionFailedException` | an external authorization handshake failed | the OAuth surface |
| `NotAuthorized` | the caller may not do this — and the one to throw when your own function refuses on authorization grounds | a workflow event's `[Authorize]`, or a slot's candidate gate |
| `RequirementsNotMet` | the WORK is not finished, which is not the same as not being allowed | a slot deposit whose `Requires` criteria are unmet |
| `DeviceUnavailableException` | the browser would not hand over a camera or microphone. `ex.Message` says which of the four reasons | `Camera.Start()` / `Mic.Start()` |
| `WorkflowError` | an awaited child workflow reached a `terminal error`. **Catch-only** — throwing it is refused | the engine |
| `WorkflowCancelled` | an awaited child workflow reached a `terminal cancel`. **Catch-only** — throwing it is refused | the engine |

Every type the platform raises on your behalf is in this table, so a `catch` always has something **knowable** to
match on. That is what lets you catch a constraint violation as an ordinary `ValidationException`
([try / catch / finally](https://osysharp.com/reference/function/try-catch/)) rather than by inspecting a database error.

### Are the .NET exceptions there too?   {#dotnet-exceptions}
Yes — the common ones, under their .NET names and with their .NET ancestry, so C# that throws or catches them compiles
unchanged. Several are raised **by the runtime itself**, on the server and in the browser alike, for exactly the
operations C# raises them for:

| Type | Derives from | Also raised for you by |
|---|---|---|
| `InvalidOperationException` | `Exception` | — |
| `ArgumentException` | `Exception` | — |
| `ArgumentNullException` | `ArgumentException` | — |
| `ArgumentOutOfRangeException` | `ArgumentException` | a `List<T>` index past its end |
| `ArithmeticException` | `Exception` | — |
| `DivideByZeroException` | `ArithmeticException` | an `int`, `long` or `decimal` divided (or `%`) by zero |
| `OverflowException` | `ArithmeticException` | an integer result that does not fit its type — Osy# integer arithmetic is CHECKED |
| `FormatException` | `Exception` | `int.Parse("abc")` and the other `Parse`s |
| `KeyNotFoundException` | `Exception` | `d[key]` with no such key |
| `IndexOutOfRangeException` | `Exception` | `xs[i]` past the end of a value declared `T[]`, and a string's or `byte[]`'s `[i]` — as in C#, where a `List<T>` raises `ArgumentOutOfRangeException` instead |
| `NotSupportedException` | `Exception` | — |
| `NotImplementedException` | `Exception` | — |
| `TimeoutException` | `Exception` | — |

```osy title="the .NET exceptions, thrown and raised" test app=function-throw-dotnet
string Faults(string? name) {
  int zero = 0;
  string seen = "";
  try { if (name == null) { throw new ArgumentNullException("name"); } }
  catch (ArgumentException) { seen = "argument"; }           // an ancestor takes it, as in C#
  try { int q = 1 / zero; }
  catch (DivideByZeroException) { seen += " · divide"; }    // raised by the runtime
  try { int big = int.MaxValue; big = big + 1; }
  catch (ArithmeticException) { seen += " · overflow"; }    // OverflowException, through its ancestor
  return seen;
}
```

⚠ `InvalidOperationException` and `NotSupportedException` are never raised FOR you from an ordinary operation: the
platform also uses those .NET types for its own faults, and a typed `catch` must not swallow one of those as if your
code had raised it. Throw them yourself freely.

### Declaring your own   {#declare-your-own}
Declare an exception exactly as in C#: a class deriving from `Exception`, or from any type in the table. A `catch`
for the type **or any of its ancestors** takes it, and `e.Type` names the class that was actually thrown:

```osy title="a family of faults of your own" test app=function-throw-own-types
class PaymentException : ExternalServiceException { }
class CardDeclinedException : PaymentException { }

string Charge(decimal amount) {
  try {
    if (amount > 1000) { throw new CardDeclinedException("the card was declined"); }
    return "charged";
  } catch (PaymentException e) {
    return e.Type + ": " + e.Message;      // "CardDeclinedException: the card was declined"
  }
}
```

- **A subtype keeps its ancestor's meaning.** `class OrderGoneException : NotFoundException { }` is still a
  not-found wherever the platform asks — an endpoint answers it with 404, as it would the base type.
- **It behaves the same in the browser.** A client action throwing and catching your type matches exactly as a server
  function does.
- **`WorkflowError` and `WorkflowCancelled` stay the engine's.** A type derived from either cannot be thrown, for the
  same reason the base cannot.
- **There is no constructor to write.** `throw new OrderGoneException("…")` passes the text as its `Message` — so the
  C# idiom `public OrderGoneException(string m) : base(m) { }` has nothing to call, and is answered with that rather
  than compiled into a constructor that would do nothing.
- **It can carry values.** Declare fields on it — numbers, text, dates, bools, enums, and other exceptions (one, or a
  `List<Exception>`) — and set them where you throw: `throw new HttpFailedException("upstream refused") { StatusCode =
  502 };`. A `catch` for that type reads `e.StatusCode`, on the server, in the browser, and after a run that suspended
  inside the catch resumes. A field of any other kind (a list of rows, an entity) is refused where you declare it,
  because it could not make that trip.

### Raising one over another   {#inner}
C#'s `new X(message, inner)` keeps the exception it was raised over, and `e.InnerException` reads it back — an
exception like any other, with its own type and message (or `null`). An `AggregateException` gathers several:
`new AggregateException(a, b)`, `new AggregateException("message", list)`; `e.InnerExceptions` is the list,
`e.InnerException` its first, and its `Message` names each one — `One or more errors occurred. (a) (b)`, as .NET's
does. It is what a blocking join on a failed task (`t.Result`, `t.Wait()`, `Task.WaitAll`), a failed task's
`Exception` and a failed `Parallel` loop raise ([async / await and Task](https://osysharp.com/reference/function/async-await/)).

```osy title="keep the cause, and read it back" test app=function-throw-inner
string Parse(string text) {
  try { return $"{int.Parse(text)}"; }
  catch (FormatException ex) { throw new InvalidOperationException($"'{text}' is not a quantity", ex); }
}

string Why(string text) {
  try { return Parse(text); }
  catch (InvalidOperationException ex) { return ex.Message + " — " + ex.InnerException.Type; }
}

int Gathered() {
  var all = new AggregateException(new ArgumentException("a"), new InvalidOperationException("b"));
  return all.InnerExceptions.Count;
}
```

⚠ **`ValidationException` is the duplicate one, however much English disagrees.** A row refused by `[Unique]` is the
entity's own declared rule saying no, so it is a `ValidationException` — not a `ConflictException`, which is about
what the DATA currently says. The two catch differently and the English pulls the wrong way, which is why the
compile error that lists these names lists what raises each beside it.

### Holding one in a variable   {#as-a-type}
A fault type is an **ordinary type**: it may stand wherever a type name may, so a local, a field, a parameter, a
return type or a generic argument can hold one. Assignment follows the same rule a typed `catch` does — every
subtype widens to `Exception`, and nothing narrows back without you saying which you meant.

```osy title="hold the fault and act on it after the try" test app=function-throw-astype
string Latest(string code) {
  Exception? caught = null;
  try { throw new ConflictException($"'{code}' moved under you"); }
  catch (Exception e) { caught = e; }
  return caught == null ? "fine" : $"{caught.Type}: {caught.Message}";
}
```

### Building one to throw later   {#as-a-value}
`new X("…")` is an ordinary expression, not a piece of `throw` syntax: build the exception wherever it is clearest —
a helper that words a refusal once, a local you fill in and throw after a loop — and `throw` it later. `throw e;`
raises **the object `e` holds**, exactly as C# does: its own type (a `catch` for any ancestor takes it), its message
and its fields. That includes a caught exception rethrown by name — `catch (Exception e) { throw e; }` does not widen
a `NotFoundException` into an `Exception`.

```osy title="one helper words the refusal; the caller throws it" test app=function-throw-as-value
class CarrierRefusedException : ExternalServiceException { public int StatusCode; }

ExternalServiceException Refused(string what, int status)
  => new CarrierRefusedException("the carrier refused " + what) { StatusCode = status };

string Book(string parcel, bool accepted) {
  if (!accepted) { throw Refused("the booking of " + parcel, 422); }
  return "booked " + parcel;
}

int StatusOf(string parcel) {
  try { Book(parcel, false); return 200; }
  catch (CarrierRefusedException e) { return e.StatusCode; }
}
```

```osy title="it is thrown as itself" run app=function-throw-as-value
[Test]
void A_built_exception_is_thrown_later_with_its_own_type_and_fields() {
  var e = Assert.Throws<ExternalServiceException>(() => Book("P-1", false));
  Assert.Equal("the carrier refused the booking of P-1", e.Message);
  Assert.Equal("CarrierRefusedException", e.Type);
  Assert.Equal(422, StatusOf("P-1"));
}
```

`WorkflowError` and `WorkflowCancelled` cannot be built as values either — the refusal a `throw new` gets applies to
the construction itself, wherever it is written.

### Catching a constraint violation is not the end of it   {#the-refused-row}
⚠ **The row the database refused is still there when your `catch` runs, and doing nothing about it is the one shape
that does not work.** This is the trap, because the code that falls into it is the code everybody writes first:

```osy title="the catch runs — and the 409 never reaches the caller" syntax
int Book(string code) {
  try {
    var b = new Order { Code = code, Total = 10m };
    return 201;
  } catch (ValidationException e) {
    return 409;              // ⛔ the catch DOES run. The refused Order is still staged, so the function
  }                          //    then faults at its own boundary with the same violation, after this
}                            //    `return` has already happened. The caller gets the platform's message.
```

Nothing is dropped for you, and that is deliberate: **it is your data.** A form that is still open on screen must be
able to take the 409, let the user change the value, and save again — so the platform holds the row and leaves the
decision to the app, because only the app knows whether its form closed as part of saving.

There are **three** things you can do about it, and which one is right is a question about your UI:

| Your form… | Do this | Why |
|---|---|---|
| **stays open** after a failed save | **amend the row** in the catch | the end settlement persists the repair — one write, the value the user fixed |
| **stays open**, and the app should say so in its own words | **`throw new ConflictException("…")`** | a throw discards ([[#rollback]]); your sentence reaches the caller and nothing is written |
| **closes** as part of save — there is nothing to get back to | **`UnitOfWork.Discard()`** first, then return | the row is gone, and the function returns your status code normally |

```osy title="all three, and each is a complete answer" test app=function-throw
// The form stays open: fix the value the user gave and let the settlement write it.
string AmendAndKeep(string code) {
  var o = new Order { Code = "provisional", Total = 10m };
  try {
    o.Code = code;
    return "written";
  } catch (ValidationException e) {
    o.Code = "provisional-2";
    return "amended";
  }
}

// Say it in the app's own words. Nothing is written.
string RefuseInMyOwnWords(string code) {
  try {
    var o = new Order { Code = code, Total = 10m };
    return "created";
  } catch (ValidationException e) {
    throw new ConflictException($"the code '{code}' is already spoken for");
  }
}

// The form closed on save. Decline the row explicitly, then answer for yourself.
int DeclineTheRow(string code) {
  try {
    var o = new Order { Code = code, Total = 10m };
    return 201;
  } catch (ValidationException e) {
    UnitOfWork.Discard();
    return 409;
  }
}
```

Proved, not asserted — the discard shape returns its own status code, and writes nothing:

```osy title="a declined row is gone, and the function returns normally" run app=function-throw
[Test]
void Discarding_in_the_catch_lets_the_function_answer_for_itself() {
  var tooLong = "0123456789012345678901234567890123456789";   // > MaxLength(20)

  Assert.Equal(409, DeclineTheRow(tooLong));   // the function's OWN status code, not the platform's fault
  Assert.Empty(Order.ToList());                // and nothing was written
}
```

⭐ **`osy lint` finds the fourth shape for you** — the catch that repairs nothing, rethrows nothing and discards
nothing — as `data-caught-write-fault-left-staged`, and its remedy names all three of the above.

### A throw discards what the function wrote   {#rollback}
This is the part worth internalising. A function is transactional ([function](https://osysharp.com/reference/function/declaration/)), and a throw is the abort:

```osy title="the first row is not left behind" test app=function-throw
void PlaceTwo(string first, string second) {
  var a = new Order { Code = first, Total = 10m };

  if (second == "") { throw new ValidationException("the second code is required"); }

  var b = new Order { Code = second, Total = 20m };
}
```

If `second` is empty, **neither order exists.** The first `new Order` was already written in the ordinary sense — it is
simply never committed. You do not unwind it, and there is no state in which the caller can observe it.

Proved, not asserted:

```osy title="a fault leaves nothing behind" run app=function-throw
[Test]
void A_throw_discards_the_rows_the_function_had_written() {
  Assert.Throws<ValidationException>(() => PlaceTwo("A1", ""));

  Assert.Empty(Order.ToList());   // NOT one row — the first `new Order` went with it
}

[Test]
void The_type_is_what_a_caller_matches_on() {
  Assert.Throws<NotFoundException>(() => Refund("nope", 1m));
}
```

### `throw` ends a path   {#definite-return}
A `throw` satisfies the compiler's "all paths return a value" rule, exactly as it does in C#. A guard clause that
throws needs no `else`:

```osy title="a guard clause, with no else" test app=function-throw
decimal TotalOf(string code) {
  var order = Order.SingleOrDefault(o => o.Code == code);
  if (order == null) { throw new NotFoundException($"no order '{code}'"); }

  return order.Total;   // reachable only when the order exists — no `else`, no null check
}
```

### What a caller outside the app sees   {#the-boundary}
Inside the app, a throw is caught by type ([try / catch / finally](https://osysharp.com/reference/function/try-catch/)).

A function reached from **outside** the app — over its published REST surface, or as a tool call — is different: a
fault becomes a failure carrying **your message**, and **the type does not survive the crossing**. So write the
message for the person who will read it, and do not expect an external caller to branch on `NotFoundException` versus
`ValidationException`. Inside, the type is everything; at the edge, the message is.

### Where can a throw stand in for a value?   {#throw-expression}
C#'s THROW EXPRESSION: on the right of `??`, as either branch of `?:`, and as an expression body (`=> throw …`). It
throws exactly what the `throw` statement throws, and only when it is reached — `s ?? throw …` is `s` whenever `s` is
present. It has no value of its own, so the expression's type is the other operand's.

```osy title="a value, or the reason there is none" test app=function-throw-expression
string NameOf(string? given) => given ?? throw new ArgumentNullException("given");
int Positive(int n) => n > 0 ? n : throw new InvalidOperationException($"{n} is not positive");
int Later() => throw new NotImplementedException("Later");
```

## See also       {#see-also}
- [try / catch / finally](https://osysharp.com/reference/function/try-catch/) — catching one, and what else can throw
- [UnitOfWork](https://osysharp.com/reference/function/unit-of-work/) — `UnitOfWork.Discard()`, the third answer to a refused row
- [function](https://osysharp.com/reference/function/declaration/) — why a fault discards the writes: a function is one transaction
- [constraints](https://osysharp.com/reference/entity/constraints/) · [invariant](https://osysharp.com/reference/entity/invariants/) — the rules that raise `ValidationException` for you
- [Assert](https://osysharp.com/reference/testing/assert/) — `Assert.Throws<T>`, which is how you prove a function refuses what it should
