E-IDLE-MISPLACED
<onIdle>
appears inside a state-child body. Engine-wide watchdog must
sit at engine root.
What it means
<onIdle>
is the engine-wide event-timeout watchdog —
armed at module-init, reset on every successful transition,
fires after N ms of silence across the whole engine. Per
SPEC §51.0.R, it MUST sit at engine-root scope (sibling of
state-children), NOT inside any state-child body.
When the compiler finds an
<onIdle>
element nested inside a state-child body, E-IDLE-MISPLACED
fires. The error message directs the developer to
<onTimeout>,
which IS the per-state alternative.
Minimal reproducer
type Phase:enum = { Active, Idle }
<engine for=Phase initial=.Active>
<Active rule=.Idle>
<onIdle after=5m to=.Idle/>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// E-IDLE-MISPLACED: <onIdle> inside a state-child body.
// Use <onTimeout> for per-state semantics, or
// move <onIdle> to engine root.
</>
<Idle/>
</>
How to fix
Two paths; pick the one that matches your intent:
Intent A: per-state timer (most common)
If you want the timer to apply only when the engine is in this specific state, you want <onTimeout> instead. Same syntax, different semantics: armed on entry, cleared on exit.
<Active rule=.Idle>
<onTimeout after=5m to=.Idle/>
// Per-state timer — armed when entering .Active, cleared on exit.
</>
Intent B: engine-wide watchdog
If you want a session-idle watchdog that resets on every
transition (including transitions back to the same state),
move
<onIdle>
to engine root:
<engine for=Phase initial=.Active>
<Active rule=.Idle>
<button onclick=refresh()>I'm here</button>
</>
<Idle/>
<onIdle after=5m to=.Idle/>
// Engine-wide — armed at module-init, reset on every transition.
</>
<onTimeout>
vs
<onIdle>
decision
- Use onTimeout when the timer is conceptually about this state: a loading state timing out, a confirmation modal auto-dismissing, a temporary success banner clearing.
- Use onIdle when the timer is conceptually about the whole engine: session-idle detection, inactivity-based logout, screen-lock after no activity.
- Both compose — an engine MAY declare both per-state onTimeout(s) AND a single onIdle watchdog. Each occupies a distinct slot in the runtime timer map.
Related
- <onIdle> — element reference.
- <onTimeout> — the per-state alternative this error redirects to.
-
E-IDLE-DUPLICATE
— companion error when more than one
<onIdle>appears in one engine. -
E-IDLE-INVALID-VARIANT
— companion error when
to=references a non-enum variant. - E-STRUCTURAL-ELEMENT-MISPLACED — generic structural-element-misuse error; E-IDLE-MISPLACED is the sharper variant.
Specification
Normative text:
SPEC §51.0.R (form + placement +
engine-wide semantics) +
§51.0.M
(per-state <onTimeout>
alternative) +
§34 catalog row. Spec lives at
compiler/SPEC.md
.