[Android] Calling inPath:measure() inside a ScriptedPathEffect kills the effect — silently
READY TO FILE. Prepared 2026-08-24 from issue dnar; minimal repro
verified on both platforms. This file is the source of record for the report
body — if maintainers ask for edits, change it here and update the issue to
match. May be re-routed to rive-app/rive-android, same as #439.
Reproduction repository: https://github.com/lsadsad/rive-android-scripted-patheffect-repro
(clone → npm install → npx expo run:android)
Summary
A Luau ScriptedPathEffect that calls inPath:measure() inside its update
stops executing entirely on Android. Not "runs and returns an empty path" —
the effect body never runs at all. The same effect, same file, same host code
works on iOS.
The trigger is narrow, and isolated to a single call. An effect that
generates geometry from scratch works fine on Android. Add one line that
measures the incoming path, and the effect dies.
Completely silent: no error, no warning, no onError, and nothing in
adb logcat beyond routine Nitro.HybridObjectPrototype lines.
The isolating experiment
One .riv, one artboard, one shape, one effect. Two versions of the same
script, each run on both platforms.
Version A — generate from scratch (ignores inPath; builds a ring segment
with moveTo/lineTo):
|
iOS |
Android |
| renders |
yes |
yes |
scriptRan |
2 |
1 |
Version B — derive from inPath (identical except it calls
inPath:measure() and measure:extract(...)):
|
iOS |
Android |
| renders |
yes |
no |
scriptRan |
3 |
0 |
inPathLen |
869.117 |
0 |
Same artboard, same paints, same host code, same runtime pins. The only
difference between A and B is that B touches inPath.
What the sentinels tell you
The effect writes back two view-model numbers, surfaced live in the app:
scriptRan — incremented at the very top of update, before any use of inPath.
inPathLen — the measured length of the incoming path.
On Android, version B reports scriptRan = 0. Since that increment happens
before measure() is ever reached, the effect body is not completing at all —
this is not a case of the body running and finding an empty path. inPathLen = 0
is consistent with that.
On iOS the same build reports scriptRan = 3, inPathLen = 869.117, and
renders the extracted sub-path correctly.
The script
function update(self: ArcEffect, inPath: PathData, node: NodeReadData): PathData
local out = Path.new()
self.count = self.count + 1 -- sentinel, BEFORE any inPath use
if self.ran then self.ran.value = self.count end
local measure = inPath:measure() -- <-- adding this line breaks Android
local total = measure.length
if self.len then self.len.value = total end
if total <= 0.0 then return out end
local percent = 0.0
if self.sweep then percent = self.sweep.value end
if percent <= 0.0 then return out end
measure:extract(0.0, total * (percent / 100.0), out, true)
return out
end
Reproduction
npm install
npx expo run:ios # renders; scriptRan climbs, inPathLen ~ 869
npx expo run:android # nothing renders; scriptRan = 0, inPathLen = 0
The probe artboard is a single shape with a placeholder triangle path, a fill
and a stroke, and the ScriptedPathEffect on both. No nested artboards, no
artboard lists, no property groups.
Environment
|
|
@rive-app/react-native |
0.4.19 |
react-native-nitro-modules |
0.35.10 |
rive-android |
11.7.2 (pinned by the package's runtimeVersions.android) |
RiveRuntime (iOS) |
6.21.1 |
| React Native |
0.85.3 (Expo SDK 56.0.18, new architecture) |
| Android |
emulator, API 36 (Android 16), arm64 — FAILS |
| iOS |
simulator, iPhone 17 Pro — works |
How we found it
This began as a production component whose donut arcs render on iOS and on web
(@rive-app/canvas) but not on Android, from an md5-identical file. That
component's effect derives its slice geometry from the incoming path via
PathMeasure/extract — the same pattern as version B.
Ruled out along the way, so you don't have to: ScriptedPathEffect being
unsupported on Android (a sibling component uses one and renders fine), Luau not
running on Android (same counter-example), binding the wrong view-model
instance, intro/timing artifacts, emulator staleness, nested-artboard
containment (the failing component has none), and stroke-vs-fill (the probe puts
the same effect on a fill and a stroke; both behave identically).
Possibly related
#439 — nested-artboard
Property Groups never advance on Android. Same runtime, same signature: renders
convincingly, silently omits the thing under test. We don't know whether they
share a root cause.
[Android] Calling
inPath:measure()inside a ScriptedPathEffect kills the effect — silentlyReproduction repository: https://github.com/lsadsad/rive-android-scripted-patheffect-repro
(clone →
npm install→npx expo run:android)Summary
A Luau ScriptedPathEffect that calls
inPath:measure()inside itsupdatestops executing entirely on Android. Not "runs and returns an empty path" —
the effect body never runs at all. The same effect, same file, same host code
works on iOS.
The trigger is narrow, and isolated to a single call. An effect that
generates geometry from scratch works fine on Android. Add one line that
measures the incoming path, and the effect dies.
Completely silent: no error, no warning, no
onError, and nothing inadb logcatbeyond routineNitro.HybridObjectPrototypelines.The isolating experiment
One
.riv, one artboard, one shape, one effect. Two versions of the samescript, each run on both platforms.
Version A — generate from scratch (ignores
inPath; builds a ring segmentwith
moveTo/lineTo):scriptRanVersion B — derive from
inPath(identical except it callsinPath:measure()andmeasure:extract(...)):scriptRaninPathLenSame artboard, same paints, same host code, same runtime pins. The only
difference between A and B is that B touches
inPath.What the sentinels tell you
The effect writes back two view-model numbers, surfaced live in the app:
scriptRan— incremented at the very top ofupdate, before any use ofinPath.inPathLen— the measured length of the incoming path.On Android, version B reports
scriptRan = 0. Since that increment happensbefore
measure()is ever reached, the effect body is not completing at all —this is not a case of the body running and finding an empty path.
inPathLen = 0is consistent with that.
On iOS the same build reports
scriptRan = 3,inPathLen = 869.117, andrenders the extracted sub-path correctly.
The script
Reproduction
The probe artboard is a single shape with a placeholder triangle path, a fill
and a stroke, and the ScriptedPathEffect on both. No nested artboards, no
artboard lists, no property groups.
Environment
@rive-app/react-nativereact-native-nitro-modulesrive-androidruntimeVersions.android)RiveRuntime(iOS)How we found it
This began as a production component whose donut arcs render on iOS and on web
(
@rive-app/canvas) but not on Android, from an md5-identical file. Thatcomponent's effect derives its slice geometry from the incoming path via
PathMeasure/extract— the same pattern as version B.Ruled out along the way, so you don't have to: ScriptedPathEffect being
unsupported on Android (a sibling component uses one and renders fine), Luau not
running on Android (same counter-example), binding the wrong view-model
instance, intro/timing artifacts, emulator staleness, nested-artboard
containment (the failing component has none), and stroke-vs-fill (the probe puts
the same effect on a fill and a stroke; both behave identically).
Possibly related
#439 — nested-artboard
Property Groups never advance on Android. Same runtime, same signature: renders
convincingly, silently omits the thing under test. We don't know whether they
share a root cause.