# Errors you have to handle


A function that can fail says so after `!`, and its caller gets a
`Result`. Errors are ordinary values, usually an enum.

`errors/main.xo`

```xo
enum ConfigErr {
  NotANumber(s: Str)
  OutOfRange(n: Int)
}

fn (e: ConfigErr) display() -> Str {
  match e {
    NotANumber(s) => "not a number: ${s}"
    OutOfRange(n) => "out of range: ${n}"
  }
}

fn parse_port(s: Str) -> Int ! ConfigErr {
  let n = Int.parse(s).or_err(ConfigErr.NotANumber(s))?
  if n < 1 || n > 65535 {
    return Err(ConfigErr.OutOfRange(n))
  }
  n
}

enum StartErr {
  Config(from ConfigErr)
}

fn start(port: Str) -> Str ! StartErr {
  let p = parse_port(port)?
  "listening on ${p}"
}

fn main(os: Os) {
  for input in ["8080", "abc", "99999"] {
    match start(input) {
      Ok(msg) => os.stdio.println(msg)
      Err(Config(e)) => os.stdio.println("error: ${e.display()}")
    }
  }
  let fallback = parse_port("x").ignore_err("the default port is fine") ?? 8080
  os.stdio.println("fallback ${fallback}")
}
```

Output:

```text
listening on 8080
error: not a number: abc
error: out of range: 99999
fallback 8080
```



- `-> Int ! ConfigErr` returns either an `Int` or a `ConfigErr`.
- `expr?` returns the error to the caller. Here `?` in `start` turns a
  `ConfigErr` into a `StartErr` through the `from` variant.
- `Int.parse(s)` returns `Int?`; `.or_err(...)` turns a missing value into an
  error.
- `match` on the result handles both outcomes.

Ignoring an error is a compile error, not a lint:

```xo
fn parse_port(s: Str) -> Int ! Error {
  Int.parse(s).or_err("not a number")?
}

fn main(os: Os) {
  parse_port("8080")
}
```

`xo check` says:

```text
main.xo:6:3: error XO0201: result of `parse_port` is not handled
  note: `parse_port` can fail with `Error`; propagate with `?`, match on it, or discard it with `.ignore_err("reason")` (core 2.6)
  fix: discard with a reason
1 module(s), 1 error(s), 0 warning(s)
```


When a failure really does not matter, say why:
`parse_port(s).ignore_err("the default port is fine")`. The reason is in the
code for every reviewer to see.

