Repository navigation
Confusion about error.execution #644
Description
The error.execution is causing me some confusion.
-
Is it true that when defining the
error_executionevent in my machine, I must manually give it anidmanually usingEvent(..., id="error.execution")?Before sending an event to the state machine, I check whether the event can be triggered using
sm.allowed_events. Then I needed to catch all unhandled exceptions that could be raised by the listeners (StateChart itself, Model, etc.). I learned from the Error Handling page that this can be done by adding handlers for theerror.executionevent. But it seemssm.allowed_eventsandsm.enabled_events()conflicts with theerror_executionevent.I will demonstrate this using the
ErrorLoggerexample from the Error Handling section:from statemachine import State, StateChart class ErrorLogger(StateChart): running = State(initial=True) failed = State(final=True) process = running.to(running, on="do_process") error_execution = running.to(failed, on="log_error") def do_process(self): raise ValueError("bad data") def log_error(self, error): self.last_error = errorWhen I try to get enabled events after the machine reaches a stable configuration:
sm = ErrorLogger() print(f" Enabled events: {sm.enabled_events()}")I get the
AttributeErrorexception:(.venv) PS <...>> sm-test.exe Traceback (most recent call last): File "<frozen runpy>", line 203, in _run_module_as_main File "<frozen runpy>", line 88, in _run_code File "<...>\.venv\Scripts\sm-test.exe\__main__.py", line 5, in <module> sys.exit(main()) ~~~~^^ File "<...>\src\sm_test\__main__.py", line 57, in main print(f" Enabled events: {sm.enabled_events()}") ~~~~~~~~~~~~~~~~~^^ File "<...>\.venv\Lib\site-packages\statemachine\statemachine.py", line 450, in enabled_events result = self._engine.enabled_events(*args, **kwargs) File "<...>\.venv\Lib\site-packages\statemachine\engines\sync.py", line 191, in enabled_events "event": getattr(sm, event), ~~~~~~~^^^^^^^^^^^ AttributeError: 'ErrorLogger' object has no attribute 'error.execution'. Did you mean: 'error_execution'?I found that the problem disappears if I manually assign the name
error.executionto theerror_executionevent:class ErrorLogger(StateChart): [...] error_execution = Event(running.to(failed, on="log_error"), id="error.execution") -
Is it true that
error_executionevent, by design, can be sent to the machine from "outside"?When I applied the workaround with
Event(..., id="error.execution"), thesm.enabled_events()gives:Enabled events: [BoundEvent('process', delay=0, internal=False), BoundEvent('error.execution', delay=0, internal=False)]Note that
error.executionis enabled event, and it can be sent to the machine frommain().This snippet:
sm = ErrorLogger() print(f" Enabled events: {sm.enabled_events()}") if "error.execution" in sm.enabled_events(): sm.send("error.execution", error=RuntimeError("Should this be possible?")) print(sm.last_error)gives:
Enabled events: [BoundEvent('process', delay=0, internal=False), BoundEvent('error.execution', delay=0, internal=False)] Should this be possible?The
error.executionevent is described as an internal mechanism of the machine, and it’s strange that this event can be sent from "outside".
python-statemachine versions 3.0.0, 3.1.0, 3.2.0, and 3.2.1 work identically with the specified code snippets.
Activity
fgmacedo commentedon Sep 13, 2026
Hi @Dolecor, thanks for the detailed report, the two questions have different answers.
1. The
AttributeErroris a bug, and theid=workaround is not the intended API.Confirmed and tracked in #654. The
error_prefix convention is supposed to register two eventnames, but
_expand_event_idexpresses that by returning a single space-separated string:The transitions themselves receive the two correct names, which is why
send("error.execution")works. What is wrong is the registration:
add_eventbinds the class attribute under the gluedid, so
getattr(sm, "error.execution")inenabled_eventsfinds nothing.It is not specific to the
Eventclass or toerror.execution. The plain assignment form isaffected too, and the same applies to the
done_state_anddone_invoke_prefixes:So
sm.error_foo()has never worked for a prefixed event, onlysend(). YourEvent(..., id="error.execution")workaround is correct for now, since an explicitidskipsthe expansion entirely, but it should not be necessary and you will not need it once #654 is
fixed.
2. Whether
error.executionshould be sendable from outside is a design question.You are right that it reads oddly. In SCXML,
error.executionis raised by the platform onto theinternal queue, so an external
send("error.execution")has no counterpart in the specification.The library currently does not distinguish platform events from user events, which is why it
shows up in
enabled_events()and accepts an external send.That one is @fgmacedo's call, so I am leaving this issue open for it. Whatever the decision, the
error.executiondocumentation should say explicitly whether the event is meant to be sent byuser code, because today nothing on the Error Handling page rules it out.
docs: state that platform events are not meant to be sent by user code
fgmacedo commentedon Oct 12, 2026
Question 2 is addressed by the docs in #671:
error.execution,done.state.<id>anddone.invoke.<id>are platform events raised by the engine, with no external counterpart in SCXML. The library does not enforce that today, so the new section states what happens when user code sends one anyway (noerrorargument reaches the handler on a baresend, and it is listed byallowed_events/enabled_events()like any other event) and shows how to filter them out when exposing events to a UI or API. No behavior change in 3.2.2.Question 1, the
AttributeErrorwith theerror_prefix, is fixed by the #654 work.fix: register one event per id a prefixed attribute expands to
fgmacedo commentedon Oct 13, 2026
Thanks again @Dolecor for the detailed report, it surfaced a real bug and a gap in the docs.
AttributeErrorfromenabled_events()and the need forid="error.execution"): fixed in fix: register one event per id a prefixed attribute expands to #675. Theerror_prefix now registers botherror_executionanderror.executionas real events, sosm.error_execution(),send("error.execution"),allowed_eventsandenabled_events()all work without the explicitid.error.executionfrom outside): documented in docs: state that platform events are not meant to be sent by user code #671. Platform events (error.*,done.state.*,done.invoke.*) are generated by the engine and are not meant to be sent by user code; the library does not block it today, and the Error Handling page now says so.Both changes ship in the upcoming 3.2.2 release (milestone: https://github.com/fgmacedo/python-statemachine/milestone/11).