Skip to content

Fix nullable float props crashing ViewManagers on Android - #58038

Closed
dennytosp wants to merge 1 commit into
react:mainfrom
dennytosp:fix/boxed-float-react-prop-setter
Closed

Fix nullable float props crashing ViewManagers on Android#58038
dennytosp wants to merge 1 commit into
react:mainfrom
dennytosp:fix/boxed-float-react-prop-setter

Conversation

@dennytosp

Copy link
Copy Markdown
Contributor

Summary:

Fixes#55350.

A codegen'd Fabric component that declares an optional float prop:

typeNativeProps=Readonly<{
...ViewProps,testFloatNullable?: WithDefault<CodegenTypes.Float,null>,}>;

crashes the app at startup as soon as the ViewManager implements it with a @ReactProp annotation:

java.lang.RuntimeException: Unrecognized type: class java.lang.Float
for method: MyNativeViewManager#setTestFloatNullable
at ViewManagersPropertyCache.createPropSetter(...)
at ViewManagerPropertyUpdater$FallbackViewManagerSetter.<init>(...)
at ViewManager.getNativeProps(...)

WithDefault<Float, null> is the only way to express a genuinely nullable float in a codegen spec — a plain optional CodegenTypes.Float is emitted as a primitive float defaulting to 0f. So codegen generates a @Nullable Float setter in the ViewManager interface, and then the runtime refuses to bind it.

Root cause

GeneratePropsJavaInterface emits exactly four boxed (nullable) prop types:

Codegen type annotationEmitted Java typeHandled by ViewManagersPropertyCache
BooleanTypeAnnotation with default: null@Nullable BooleanBoxedBooleanPropSetter
ReservedPropTypeAnnotation / ColorPrimitive@Nullable IntegerBoxedColorPropSetter
Int32EnumTypeAnnotation@Nullable IntegerBoxedIntPropSetter
FloatTypeAnnotation with default: null@Nullable Floatnothing — falls through to else

createPropSetter maps java.lang.Boolean and java.lang.Integer, but has no branch for java.lang.Float, so it hits the else and throws. This adds the missing BoxedFloatPropSetter, mirroring BoxedIntPropSetter: unbox Double (all JS numbers arrive as Double) to Float, and pass null straight through so the setter actually receives the absent value.

DoubleTypeAnnotation is deliberately not covered — codegen always emits it as a primitive double, never boxed, so there is no nullable-double setter to bind.

Note this only affects ViewManagers that use @ReactProp. A codegen'd ViewManager that routes props through its generated delegate never reaches ViewManagersPropertyCache, which is why the crash looks intermittent — the annotation is what pulls in FallbackViewManagerSetter.

Changelog:

[ANDROID] [FIXED] - Fix RuntimeException: Unrecognized type: class java.lang.Float when a ViewManager implements a nullable float prop (WithDefault<Float, null>) with @ReactProp

Test Plan:

Added testBoxedFloatSetter to ReactPropAnnotationSetterTest, alongside the existing testBoxedBooleanSetter / testBoxedIntSetter, plus a boxedFloatProp on the ViewManager under test. It drives viewManager.updateProperties(...), which is the same FallbackViewManagerSettergetNativePropSettersForViewManagerClasscreatePropSetter path as the crash stack trace, and asserts the setter receives 3.5f, -7.0f and null.

$ ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest \
--tests "com.facebook.react.uimanager.ReactPropAnnotationSetterTest"
BUILD SUCCESSFUL in 3m 16s
tests=18 failures=0 errors=0 skipped=0
ok testBoxedBooleanSetter
ok testBoxedFloatSetter <- new
ok testBoxedIntSetter
ok testFloatSetter
... (18/18)

Reverting only ViewManagersPropertyCache.kt and keeping the new test reproduces the reported crash verbatim:

tests=18 failures=18 errors=0
java.lang.RuntimeException: Unrecognized type: class java.lang.Float
for method: ReactPropAnnotationSetterTest$ViewManagerUnderTest#setBoxedFloatProp

All 18 fail rather than just the new one, and that is the bug's real shape:
getNativePropSettersForViewManagerClass builds the setter map for the entire
ViewManager class in one pass and caches it, so a single unbindable prop takes
down every other prop on that manager. That is why the reported crash happens at
startup, while getNativeProps() is collecting view manager constants, rather
than when the offending prop is first set.

Kotlin formatting:

$ ./gradlew ktfmtCheck
BUILD SUCCESSFUL

ViewManagersPropertyCache is an internal object and BoxedFloatPropSetter is
a private class, so ReactAndroid.api is unchanged.

A codegen spec that declares an optional float with
`WithDefault<CodegenTypes.Float, null>` makes codegen emit a
`@Nullable Float` setter on the generated ViewManager interface. If the
ViewManager implements that setter with `@ReactProp`, the app crashes
during startup with:
RuntimeException: Unrecognized type: class java.lang.Float
for method: MyNativeViewManager#setTestFloatNullable
`ViewManagersPropertyCache.createPropSetter` maps `java.lang.Boolean` and
`java.lang.Integer` to boxed prop setters, but has no branch for
`java.lang.Float`, so it falls through to the `else` and throws. Those
three plus `@Nullable Integer` for colors are the only boxed types
codegen can emit, which makes Float the single gap.
Add the missing `BoxedFloatPropSetter`, mirroring `BoxedIntPropSetter`:
unbox the `Double` that every JS number arrives as, and pass `null`
through untouched. `DoubleTypeAnnotation` needs no equivalent because
codegen always emits it as a primitive `double`.
Fixesreact#55350
@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 21, 2026
@facebook-github-toolsfacebook-github-toolsBot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Aug 21, 2026
@meta-codesync

Copy link
Copy Markdown

@javache has imported this pull request. If you are a Meta employee, you can view this in D116928062.

@meta-codesyncmeta-codesyncBot added the Merged This PR has been merged. label Aug 21, 2026
@meta-codesync

Copy link
Copy Markdown

@javache merged this pull request in 8d23b14.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.MergedThis PR has been merged.Shared with MetaApplied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RuntimeException: Unrecognized type: class java.lang.Float for method: SomeViewManager#setProp

1 participant

@dennytosp