Summary
When calling artboard.createStateMachine(nil) on an artboard with no state machines, the Swift layer constructs and returns a StateMachine handle backed by a null C++ object. That zombie handle gets registered in the CommandServer and produces a continuous stream of errors on every display-link frame:
ERROR : Could not create state machine with name "" because it was not found.
ERROR : State machine 0x1 not found for binding view model.
ERROR : State machine 0x1 not found for advance.
Steps to Reproduce
- Have a
.riv file whose default artboard has zero state machines.
- In the async runtime, call
artboard.createStateMachine() (i.e. name: nil).
- Pass the returned
StateMachine to Rive(file:artboard:stateMachine:...).
- Observe continuous
"State machine 0x1 not found for advance" errors on every display-link tick.
Root Cause
createStateMachine(nil) correctly routes to createDefaultStateMachine in StateMachineService (index-based via stateMachineAt(0)) — not a name-based "" lookup as the error message might suggest. The error message format just happens to say name "" even for the index-based path when it fails.
The bug is that when stateMachineAt(0) returns null (artboard has no SMs), the Swift layer does not validate this before constructing and returning a StateMachine handle. The named path already validates via getStateMachineNames() and throws ArtboardError.invalidStateMachine — the nil/default path has no equivalent null-check.
Expected Behavior
createStateMachine(nil) should throw ArtboardError.invalidStateMachine when the artboard has no state machines, consistent with how the named path handles missing SMs.
Actual Behavior
A zombie StateMachine handle is returned. No Swift error is thrown. The zombie is advanced every frame, producing continuous runtime errors until the artboard is torn down.
Suggested Fix
Add a null-check in the nil/default path — after createDefaultStateMachine resolves, validate that the resulting handle is non-null before returning. If null, throw ArtboardError.invalidStateMachine (matching the named path's error behavior).
Additional Note
This also exposes a regression in supported file configurations vs. the legacy API — see the earlier comment for details.
Environment
- rive-ios version: 6.19+
- Platform: iOS
Summary
When calling
artboard.createStateMachine(nil)on an artboard with no state machines, the Swift layer constructs and returns aStateMachinehandle backed by a null C++ object. That zombie handle gets registered in theCommandServerand produces a continuous stream of errors on every display-link frame:Steps to Reproduce
.rivfile whose default artboard has zero state machines.artboard.createStateMachine()(i.e.name: nil).StateMachinetoRive(file:artboard:stateMachine:...)."State machine 0x1 not found for advance"errors on every display-link tick.Root Cause
createStateMachine(nil)correctly routes tocreateDefaultStateMachineinStateMachineService(index-based viastateMachineAt(0)) — not a name-based""lookup as the error message might suggest. The error message format just happens to sayname ""even for the index-based path when it fails.The bug is that when
stateMachineAt(0)returns null (artboard has no SMs), the Swift layer does not validate this before constructing and returning aStateMachinehandle. The named path already validates viagetStateMachineNames()and throwsArtboardError.invalidStateMachine— the nil/default path has no equivalent null-check.Expected Behavior
createStateMachine(nil)should throwArtboardError.invalidStateMachinewhen the artboard has no state machines, consistent with how the named path handles missing SMs.Actual Behavior
A zombie
StateMachinehandle is returned. No Swift error is thrown. The zombie is advanced every frame, producing continuous runtime errors until the artboard is torn down.Suggested Fix
Add a null-check in the nil/default path — after
createDefaultStateMachineresolves, validate that the resulting handle is non-null before returning. If null, throwArtboardError.invalidStateMachine(matching the named path's error behavior).Additional Note
This also exposes a regression in supported file configurations vs. the legacy API — see the earlier comment for details.
Environment