(validations)=
The library validates your statechart structure at two stages: class definition time (when the Python class body is evaluated) and instance creation time (when you call the constructor). These checks catch common mistakes early — before any event is ever processed.
All validation errors raise InvalidDefinition.
>>> from statemachine import StateChart, State
>>> from statemachine.exceptions import InvalidDefinitionThese checks run as soon as the class body is evaluated by the
StateMachineMetaclass. If any check fails, the class itself is not
created.
Every statechart must have exactly one initial state at the root level:
>>> try:
... class Bad(StateChart):
... a = State(initial=True)
... b = State(initial=True)
... go = a.to(b)
... except InvalidDefinition as e:
... print(e)
There should be one and only one initial state. Your currently have these: a, bFinal states represent completion — outgoing transitions are not allowed:
>>> try:
... class Bad(StateChart):
... draft = State(initial=True)
... closed = State(final=True)
... reopen = closed.to(draft)
... close = draft.to(closed)
... except InvalidDefinition as e:
... print(e)
Cannot declare transitions from final state. Invalid state(s): ['closed'](unreachable-states)=
Every state must be reachable from the initial state. Isolated states indicate a wiring mistake:
>>> try:
... class Bad(StateChart):
... red = State(initial=True)
... green = State()
... hazard = State()
... cycle = red.to(green) | green.to(red)
... blink = hazard.to.itself()
... except InvalidDefinition as e:
... print(e)
There are unreachable states. The statemachine graph should have a single component. Disconnected states: ['hazard']Disable with validate_disconnected_states = False.
(trap-states)=
Every non-final state must have at least one outgoing transition. A state with no way out is a "trap" — likely a forgotten transition:
>>> try:
... class Bad(StateChart):
... red = State(initial=True)
... green = State()
... hazard = State()
... cycle = red.to(green) | green.to(red)
... fault = red.to(hazard) | green.to(hazard)
... except InvalidDefinition as e:
... print(e)
All non-final states should have at least one outgoing transition. These states have no outgoing transition: ['hazard']Disable with validate_trap_states = False:
>>> class Accepted(StateChart):
... validate_trap_states = False
... red = State(initial=True)
... green = State()
... hazard = State()
... cycle = red.to(green) | green.to(red)
... fault = red.to(hazard) | green.to(hazard)When final states exist, every non-final state must have at least one path to a final state:
>>> try:
... class Bad(StateChart):
... draft = State(initial=True)
... abandoned = State()
... closed = State(final=True)
... produce = draft.to(abandoned) | abandoned.to(abandoned)
... close = draft.to(closed)
... except InvalidDefinition as e:
... print(e)
All non-final states should have at least one path to a final state. These states have no path to a final state: ['abandoned']Disable with validate_final_reachability = False.
Internal transitions must target the same state (self) or a descendant — they cannot cross to external states:
>>> try:
... class Bad(StateChart):
... a = State(initial=True)
... b = State(final=True)
... go = a.to(b, internal=True)
... except InvalidDefinition as e:
... assert "Not a valid internal transition" in str(e)Initial transitions (automatically generated for the initial state) cannot carry conditions or events — they always fire unconditionally.
The donedata parameter can only be used on states marked as final=True:
>>> try:
... class Bad(StateChart):
... a = State(initial=True, donedata="get_data")
... b = State(final=True)
... go = a.to(b)
... except InvalidDefinition as e:
... print(e)
'donedata' can only be specified on final states.Entries in the listeners class attribute must be classes, callables, or
object instances — not primitives like strings or numbers:
>>> try:
... class Bad(StateChart):
... listeners = ["not_a_listener"]
... a = State(initial=True)
... b = State(final=True)
... go = a.to(b)
... except InvalidDefinition as e:
... assert "Invalid entry in 'listeners'" in str(e)These checks run when you instantiate a statechart (call MyChart()).
They verify that the runtime wiring is correct — callbacks resolve to
actual methods, boolean expressions parse, etc.
Every callback name declared on a transition or state (via on, before,
after, enter, exit, cond, etc.) must resolve to an actual attribute
on the statechart, model, or one of the registered listeners.
>>> class MyChart(StateChart):
... a = State(initial=True)
... b = State(final=True)
... go = a.to(b, on="nonexistent_method")
>>> try:
... MyChart()
... except InvalidDefinition as e:
... assert "Did not found name 'nonexistent_method'" in str(e)This validation ensures there are no typos in callback names. It checks all sources in order: the statechart class itself, then the model (if provided), then each listener.
Convention-based callbacks (like `on_enter_<state>` or `before_<event>`)
are **not** validated — they are optional by design. Only explicitly
declared callback names (passed as strings to `on`, `cond`, etc.) are
checked.
Guard conditions written as boolean expressions must be syntactically valid:
>>> try:
... class MyChart(StateChart):
... a = State(initial=True)
... b = State(final=True)
... go = a.to(b, cond="valid_a and valid_b")
... def valid_a(self):
... return True
... def valid_b(self):
... return True
... sm = MyChart()
... sm.send("go")
... except InvalidDefinition:
... pass # would fail if expression didn't parse
>>> "b" in sm.configuration_values
TrueExpressions support and, or, not, and parentheses. See
{ref}guards for the full syntax.
| Validation | When | Configurable |
|---|---|---|
| Exactly one initial state | Class definition | No |
| No transitions from final states | Class definition | No |
| Unreachable states | Class definition | validate_disconnected_states |
| Trap states | Class definition | validate_trap_states |
| Final state reachability | Class definition | validate_final_reachability |
| Internal transition targets | Class definition | No |
| Initial transitions have no cond | Class definition | No |
donedata on final states only |
Class definition | No |
| Invalid listener entries | Class definition | No |
| Callback resolution | Instance creation | No |
| Boolean expression parsing | Instance creation | No |
All configurable flags default to True. Set them to False on the class
to disable the corresponding check.