STM32WBA Sequencer Waits: A Pending Outer Event Cannot Unwind a Nested Wait
A sequencer task can remain inside its event wait after its own event has been set. If that wait has scheduled another task which is now waiting for a different event, the inner call must finish before execution can return to the outer task. The outer event may be pending correctly throughout that interval.
This is a concrete STM32WBA integration issue, not an RTOS ready-list rule. The CubeWBA v1.10.0 sequencer source is pinned at commit e455b860ceb52aaa0332a98f1e7a10bbd7014fc9. Its NUCLEO-WBA55CG HeartRate application calls UTIL_SEQ_Run() from MX_APPE_Process() and registers module tasks. The analysis assumes the default weak UTIL_SEQ_EvtIdle() implementation; application overrides require their own call-graph review.
Draw task functions on one call stack
A sequencer task is a registered function invoked by UTIL_SEQ_Run(). Waiting does not create an independent task stack or suspend a thread for an RTOS to resume elsewhere. The default event-idle hook calls the scheduler again from inside the current wait.
ST’s Sequencer guide describes this recursive scheduling explicitly. That makes the call stack the first useful diagnostic artifact. A display that shows only task A’s event bit misses the task B frame still above A on the same stack.
Use distinct registered tasks and distinct event bits in the example. Assume no other consumer clears either bit. These conditions avoid confusing nested completion with event-bit reuse or a separate lost-notification bug.
Follow what each wait saves and replaces
UTIL_SEQ_WaitEvt() saves the current task identity and the previous EvtWaited value. It replaces EvtWaited with its newly requested event, then loops while that event is absent from EvtSet.
While waiting, it calls UTIL_SEQ_EvtIdle(). The default hook invokes UTIL_SEQ_Run(~TaskId_bm), excluding the waiting task from that nested scheduling pass. UTIL_SEQ_Run() combines the supplied mask with its existing SuperMask, so the nested call cannot expand the enclosing set of eligible tasks.
When the requested event finally permits the wait to finish, the function restores the saved task identity, clears its own requested event bit, and restores the previous EvtWaited value. These saved values live in nested C activations. Restoring the outer context requires returning through the inner context first.
Set the outer event while the inner task waits
Start with A running and then waiting for EA. Its default idle hook re-enters the scheduler. A distinct eligible task B runs and calls UTIL_SEQ_WaitEvt(EB).
The active chain now contains A, A’s wait, the nested scheduler, B and B’s wait. The innermost wait tests EB. If an independent producer sets only EA, that does not satisfy B’s loop condition. The innermost scheduler also observes the current waited event, which is B’s event.
A waits for EA
nested scheduler runs B
B waits for EB
producer sets EA: EA remains pending; B still waits
producer sets EB: B's wait may return
B finishes and returns
nested scheduler returns toward A
A observes EA and its wait returns

The trace assumes B eventually returns after its wait. Setting EB does not skip B’s remaining code. Lengthy work after B’s wait can still delay A, even when both event bits have already been set.
A pending EA therefore need not be lost or ignored incorrectly. It is insufficient to unwind an activation that is still waiting for EB. Logging an outer event and expecting an immediate outer continuation applies the wrong execution model.
Review the producer of the deepest event
Identify who can produce EB while B’s nested wait is active. A supported interrupt or another eligible task can provide independent progress. In contrast, if EB can only be set by A’s code after A’s wait, the dependency is circular: A cannot reach that code until B returns, and B cannot return until that code runs.
Raising A’s task priority does not change this return dependency. Nor should the application override the idle hook merely to re-enter A while its earlier invocation remains waiting. That creates additional reentrancy and state-lifetime obligations rather than returning the original call.
Where the operation permits it, an explicit state machine can start work, preserve its owned state and return from the task function. A later event can schedule a continuation that checks the operation’s current state. This is a design alternative, not a drop-in transformation: retained data, cancellation and duplicate notifications still need defined ownership.
Qualify the return order with bounded instrumentation
A proposed fixture records task entry, wait entry, event set, wait return and task return for A and B. First set only EA and observe that no A-return marker is expected while B remains inside its wait. Then independently set EB, let B finish and verify the expected unwind order.
Use a recoverable test setup and a bounded observation window. Do not deliberately strand a production task to demonstrate the cycle. Also review the maximum intended nesting depth: recursive scheduling adds frames to the same stack, but this article supplies no measured stack requirement.
No sequencer execution, timing trace or watermark test was performed. The deliverable is a source-derived call-chain proof and an event-producer independence check. It explains why an outer event can be present without the corresponding outer function being able to resume yet.