Skip to content

Can't Happen

Leon Starr edited this page Aug 4, 2026 · 3 revisions

Handling a Can't Happen

A can't happen says that an event cannot legitimately arrive in this state at all. There is no prescribed model level recovery, so if a running system does deliver one, the model execution platform is left to clean up the mess.

There are two ways a state ends up with a can't happen response, and the difference matters.

Declaring it, with a reason

List the event under the state's can't happen section and say why:

state OPENING
activity
transitions
    Door opened > OPEN
    Passenger close > CLOSING
can't happen
    Door closed
        Door is opening, so it should not detect ‘closed’.
    Door blocked
        Block can only be reported while closing the door. (door jams while opening are
        not modeled, in this release anyway)
    Unlock
        <Cabin not moving> Cabin is not able to progress to a state where it can send this event while we are
        opening the door.

The grammar:

cant_happens = cant_happen_header response*
cant_happen_header = "can't happen" EOL*
response = INDENT event_name (SP tag)? EOL* explanation?

An unindented can't happen keyword opens the section — the contracted spelling, always — each event name follows on its own indented line, and its explanation is indented one level further — or, where the explanation is only a reference to a reason defined elsewhere, on the event line itself. Like ignore, the section has no -- of its own; the -- closing the state block closes it. See State block grammar and Reason tags.

Leaving it out, which is the default

Any declared event a state says nothing about is a can't happen. Omission is the default response, so a state block never has to enumerate every event it cannot receive.

That default is not an oversight, but it does mean the file cannot by itself distinguish "I thought about this and it genuinely cannot happen" from "nobody has looked at this yet". What resolves the ambiguity is how the event was declared in the Events block:

  • A completion event with no declared response cannot be received here, by definition — it belongs to whichever state issues it. Nothing to decide and nothing to explain, so leaving it out is correct and the overwhelming majority of omissions are this.
  • An interaction event with no declared response is an open question. It can arrive at any time, so somebody still has to decide whether it is ignored, transitions somewhere, or genuinely cannot happen — and say why.

So the guidance is simple: let completion events default, and declare every interaction event explicitly. Tooling reading the events block can then list the interaction events still awaiting a decision.

Can't happen is not ignore

An Ignore means the event legitimately arrives and the right response is to do nothing. A can't happen means the event has no business arriving at all. Both are non-transition responses and both take an explanation, but they describe opposite situations — see Transitions for the three responses side by side.

What the parser gives you

A cant_happens list on StateBlock_a, holding one Response_a per declared event with event, tag and explanation fields. Events left to the default do not appear anywhere in the parse result; a consumer derives them by subtracting every declared response from the events block.

Clone this wiki locally