# `BB.Error.State.StaleEpoch`
[🔗](https://github.com/beam-bots/bb/blob/main/lib/bb/error/state/stale_epoch.ex#L5)

Command refused because it does not belong to the current arming session.

`BB.Safety.armed?/1` answers "is the robot armed now?", which is not the
same question as "has the robot been armed continuously since this command
was created?". Without the second answer a command created during one
arming session could still be applied during a later one, after an
intervening disarm that was supposed to make the robot safe.

So `BB.Actuator`'s send functions stamp each outgoing command with the arm
epoch current at the moment it is sent, and `BB.Actuator.Server` refuses it
if the robot has since moved on. A command carrying no epoch at all is
refused here too: an unstamped command cannot be shown to belong to the
session that is running, and hand-built commands delivered straight to an
actuator are exactly the case the epoch exists to catch.

This is a `:state` error rather than a `:safety` one: the safety system
working as intended is not a safety violation.

# `t`

```elixir
@type t() :: %BB.Error.State.StaleEpoch{
  __exception__: term(),
  actuator: atom(),
  bread_crumbs: term(),
  class: term(),
  command: module(),
  current_epoch: pos_integer() | nil,
  epoch: pos_integer() | nil,
  path: term(),
  robot: module() | nil,
  splode: term(),
  stacktrace: term(),
  vars: term()
}
```

# `exception`

```elixir
@spec exception(opts :: Keyword.t()) :: %BB.Error.State.StaleEpoch{
  __exception__: term(),
  actuator: term(),
  bread_crumbs: term(),
  class: term(),
  command: term(),
  current_epoch: term(),
  epoch: term(),
  path: term(),
  robot: term(),
  splode: term(),
  stacktrace: term(),
  vars: term()
}
```

Create an `Elixir.BB.Error.State.StaleEpoch` without raising it.

## Keys

- :robot
- :actuator
- :command
- :epoch
- :current_epoch

---

*Consult [api-reference.md](api-reference.md) for complete listing*
