fix: add null safety guard to Brush.getVal() on Android#2940
Open
developerdanx wants to merge 1 commit into
Open
fix: add null safety guard to Brush.getVal() on Android#2940developerdanx wants to merge 1 commit into
developerdanx wants to merge 1 commit into
Conversation
When the JS thread is slow to send gradient coordinate props (x1, y1, x2, y2) to the native UI thread, the SVGLength values in the mPoints array can be null when setupPaint() is called. This causes a NullPointerException crash in Brush.getVal(). This was discovered in production at Factorial, where a large OTA bundle slowed JS execution enough to expose the race condition, causing ~17K crash events across ~8K Android users on screens rendering SVGs with LinearGradient. The fix adds a null check that returns 0 as a safe default, allowing the gradient to render gracefully once coordinates arrive.
This was referenced May 3, 2026
andrlut
added a commit
to andrlut/rpgtasks
that referenced
this pull request
May 3, 2026
…store Manrope on iOS/web (#70) Hero kept native-crashing on Android even with ErrorBoundary in place. A native crash from react-native-svg's C++/Java side bypasses the JS boundary — there's no way to catch it in JS. The user reported the app still force-closes after PR #69 OTA'd. Pivot: on Android, never construct any react-native-svg components. Render HexChartFallback (pure-RN Views/Text) directly. Same data, same look-and-feel for the dim cards / pips, just without the radar polygon. iOS and web continue to render the proper HexChart with Manrope on SvgText labels. Both platforms still wear the ErrorBoundary so any JS-level throw drops in inline. Restored fontFamily="Manrope_800ExtraBold" on the SvgText labels per user request — the rest of the app already uses Manrope and the inconsistency was visible. Re-enable the SVG path on Android once react-native-svg ships the Brush.getVal null guard (PR software-mansion/react-native-svg#2940) and we resolve native typeface registration for SVG text. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a null check in
Brush.getVal()to prevent aNullPointerExceptioncrash on Android when gradient coordinate props (x1,y1,x2,y2) have not yet been initialized by the JS thread.Problem
When the React Native JS thread is slow to deliver gradient coordinate properties to the native UI thread,
LinearGradientView.saveDefinition()creates aBrushwith nullSVGLengthentries inmPoints. When the native renderer then callssetupPaint()→getVal(), it passes these null values toPropHelper.fromRelative(), causing an immediateNullPointerExceptioncrash.This affects all SVG components using
<LinearGradient>or<RadialGradient>on Android. iOS is unaffected due to its different rendering pipeline.Crash stack trace
Production Impact
This was discovered in production at Factorial, where a large OTA bundle (caused by a temporary
EXPO_NO_BUNDLE_SPLITTING=1workaround) slowed JS thread execution enough to expose this race condition. The result was ~17,000 crash events across ~8,000 Android users on core screens that render SVGs with<LinearGradient>.The crash did not occur on native APK builds because pre-compiled Hermes bytecode executes fast enough to beat the UI draw cycle. It only manifested on OTA updates where the JS bundle loads more slowly.
Fix
A simple null guard in
getVal()that returns0as a safe default whenSVGLengthis null. This allows the gradient to render gracefully (with default coordinates) rather than crashing, and the correct coordinates are applied once the JS thread delivers them.Testing
This is a race condition between the JS thread and the native UI thread, which makes it inherently difficult to reproduce in a deterministic test environment.
What we did:
pnpm patchin our production app at FactorialEXPO_NO_BUNDLE_SPLITTING=1)What we didn't do:
getVal()method is a good candidate for a plain JUnit test (no Android framework dependencies), and we're happy to add test infrastructure in this PR if maintainers prefer it.