Skip to content

[Android] Nested-artboard Property Groups in an ArtboardComponentList never advance — silent no-op (iOS + web OK) #439

Description

@lsadsad

Reproduction repository (clone → npm installnpx expo run:android):
https://github.com/lsadsad/rive-android-nested-pg-repro — contains both .riv files, the probe component, and a README with the observed logs. Verified end-to-end on 2026-08-23: fresh install from public npm, 38 s build, both probes reproduce on an API 36 emulator while iOS passes.

Summary

A Property Group keyed on a nested item artboard, instanced through an ArtboardComponentList bound to a view-model list, never advances on Android. The file loads, the view model binds, the list resolves, and property writes land and read back correctly — but the group's animated property stays at its initial value forever. The identical JS code and identical .riv binaries work correctly on iOS and on web (@rive-app/canvas).

The failure is completely silent: no error, no warning, no onError callback — the animation simply never runs. On a production screen this presents as "loads but sits there."

Environment

@rive-app/react-native 0.4.19
react-native-nitro-modules 0.35.10
rive-android 11.7.2 (as pinned by the package's runtimeVersions.android)
RiveRuntime (iOS) 6.21.1 (pinned by runtimeVersions.ios, verified in Podfile.lock)
React Native 0.85.3 (Expo SDK 56.0.18, new architecture)
Android emulator, API 36 (Android 16), arm64 — FAILS
iOS simulator, iPhone 15 Pro — works
Web reference @rive-app/canvas 2.39.1 — works

The .riv structure (two binaries, 861 B and 772 B)

Parent artboard PGNestProbe, state machine PGNestSM, view model with a list property items (3 items). The list drives an ArtboardComponentList; each item is a nested item artboard whose view model has:

  • progress (number) — animated 0 → 100 over 2000 ms by a Property Group keyed on the nested item artboard
  • phase (number) — gate input (main binary only)

Two variants isolate conditionality:

  1. Gated (pg_nest_probe.riv, md5 d96f10b1): item state machine has ItemIdle --[phase >= 1]--> ItemRun. The host writes phase := 1 per item, staggered 700 ms apart, and polls progress per item.
  2. Autoplay discriminator (pg_nest_probe_pregate.riv, md5 002f3738): built before the gate existed — no phase property, no ItemIdle state, no condition anywhere. Each item artboard runs entry → ItemRun and should simply play.

Reproduction (minimal shape)

const { riveFile } = useRiveFile(require('./pg_nest_probe.riv'));
const { instance } = useViewModelInstance(riveFile, { async: true });
const { length, getInstanceAt } = useRiveList('items', instance);

// capture the 3 item instances once length resolves ...
const { value: p0 } = useRiveNumber('progress', items[0]);           // per-item read
const { value: ph0, setValue: setPh0 } = useRiveNumber('phase', items[0]); // per-item write

// render
<RiveView file={riveFile} dataBind={instance}
  artboardName="PGNestProbe" stateMachineName="PGNestSM" autoPlay />
// then: setPh0(1) (staggered per item), poll p0/p1/p2 every ~250 ms

The full probe component (single file, includes the verdict harness that produced the logs below) is App.tsx in the repro repository above.

Expected

progress ramps 0 → 100 per item; with the staggered gate writes the three items diverge (per-instance state). This is exactly what happens on iOS and web.

Actual

iOS — works (same day, same code, same binaries)

[PGNestNitro] list 'items' length=3; captured 3 instances
[PGNestNitro] phase[0] := 1
[PGNestNitro] t=254ms  [10.83, 0, 0]
[PGNestNitro] phase[1] := 1
[PGNestNitro] t=786ms  [37.50, 2.50, 0]
[PGNestNitro] phase[2] := 1
[PGNestNitro] t=1554ms [75.83, 40.83, 5.83]
[PGNestNitro] divergence read: [79.17, 43.33, 8.33]   ← three distinct values, staggered as written
[PGNestNitro] t=3654ms [100, 100, 100]

Android — gated binary: writes land, nothing advances

[PGNestNitro] list 'items' length=3; captured 3 instances
[PGNestNitro] phase[0] := 1
[PGNestNitro] t=274ms  [0,0,0]
[PGNestNitro] phase[1] := 1
[PGNestNitro] phase[2] := 1
[PGNestNitro] t=1605ms [0,0,0]
...
[PGNestNitro] t=4787ms [0,0,0]        ← 4.6 s, zero movement
phase reads back [1,1,1]              ← the writes DID land

progress reads are real zeros (values resolve; they are not undefined/null), and phase reads back 1/1/1 — so binding, list traversal, per-item addressing, writes, and reads all work. Only the advance is missing.

Android — autoplay discriminator: eliminates conditions entirely

[Pregate] vm: loading=false instance=ready err=null
[Pregate] list 'items' length=3; captured 3
[Pregate] t=243ms  [0,0,0]
...
[Pregate] t=3053ms [0,0,0]
VERDICT: progress never moved (spread 0.00) on an AUTOPLAYING build with
no condition anywhere.

With no gate, no condition, and no host writes at all, the nested group still never advances on Android. This rules out "the transition condition doesn't resolve against a list-item instance" — the group itself does not advance.

Ruled out

  • Stale install / stale native runtime — single canonical install verified before each run; fresh expo run:android build.
  • Asset drift — md5-verified binaries, served via Metro on both platforms.
  • Probe/verdict artifact — the harness treats unresolved reads as INCONCLUSIVE, never as zeros; the Android zeros are resolved readings. The same harness produced PASS on iOS the same day.
  • JS-layer difference — identical component code on both platforms; the divergence is below the JS bridge.

Notes

  • Everything around the advance works on Android: RiveFile load, useViewModelInstance, useRiveList + getInstanceAt, per-item useRiveNumber reads and writes (with listeners firing for phase).
  • The failure mode is the dangerous kind: silent. If a runtime can't advance nested Property Groups in an ArtboardComponentList, an error or console warning at bind time would save integrators days.
  • Everything needed to reproduce is in https://github.com/lsadsad/rive-android-nested-pg-repro; happy to provide additional logs or run variants on request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions