Skip to content

fix(android): guard against null ReadableArray in ReactBridgeHelper.toJavaList (W-23492982)#481

Merged
wmathurin merged 1 commit into
forcedotcom:devfrom
wmathurin:fix-toJavaList-npe
Jul 18, 2026
Merged

fix(android): guard against null ReadableArray in ReactBridgeHelper.toJavaList (W-23492982)#481
wmathurin merged 1 commit into
forcedotcom:devfrom
wmathurin:fix-toJavaList-npe

Conversation

@wmathurin

Copy link
Copy Markdown
Contributor

Summary

  • Add null guard in ReactBridgeHelper.toJavaList — returns empty list instead of NPE when a null ReadableArray is passed from the JS bridge
  • Add early-return in SmartStoreReactBridge.upsertSoupEntries when the entries list is empty, returning an empty JSON array to the callback

Root Cause

toJavaList called array.size() with no null check. When JavaScript passed null (or omitted) the entries argument to upsertSoupEntries, args.getArray(ENTRIES) returned null and the NPE followed immediately.

GUS

W-23492982

Reported via

forcedotcom/SalesforceMobileSDK-Android#2889

…oJavaList (W-23492982)

toJavaList called array.size() without a null check, causing a NPE when
the JS bridge passed null for the entries argument in upsertSoupEntries.
Added null guard in toJavaList and an early-return in upsertSoupEntries.
@wmathurin

Copy link
Copy Markdown
Contributor Author

Checked the iOS equivalent (SFSmartStoreReactBridge.mm) — no parallel fix needed there. upsertSoupEntries fetches entries via sfsdk_nonNullObjectForKey:, which already converts NSNull to nil safely. More importantly, Objective-C's nil-messaging semantics mean that calling [store upsertEntries:nil toSoup:...] is a silent no-op returning nil/0 — it never throws. Java's null.size() is what makes the Android fix necessary; iOS is inherently protected by the ObjC runtime.

@github-actions

Copy link
Copy Markdown
TestsPassedSkippedFailed ❌️
iOS ^18 Test Results33 ran33 ❌
TestResult
iOS ^18 Test Results
ReactMobileSyncTests.testCleanResyncGhosts()❌ failure
ReactMobileSyncTests.testGetSyncStatusDeleteSync()❌ failure
ReactMobileSyncTests.testReSync()❌ failure
ReactMobileSyncTests.testSyncDown()❌ failure
ReactMobileSyncTests.testSyncUp()❌ failure
ReactNetTests.testCollectionCreateDeleteRetrieve()❌ failure
ReactNetTests.testCollectionCreateRetrieve()❌ failure
ReactNetTests.testCollectionUpsertUpdateRetrieve()❌ failure
ReactNetTests.testCreateDelRetrieve()❌ failure
ReactNetTests.testCreateRetrieve()❌ failure
ReactNetTests.testDescribe()❌ failure
ReactNetTests.testDescribeGlobal()❌ failure
ReactNetTests.testDescribeLayout()❌ failure
ReactNetTests.testGetApiVersion()❌ failure
ReactNetTests.testMetaData()❌ failure
ReactNetTests.testPublicApiCall()❌ failure
ReactNetTests.testQuery()❌ failure
ReactNetTests.testResources()❌ failure
ReactNetTests.testSearch()❌ failure
ReactNetTests.testUpsertUpdateRetrieve()❌ failure
ReactNetTests.testVersions()❌ failure
ReactOAuthTests.testGetAuthCredentials()❌ failure
ReactSmartStoreTests.testClearSoup()❌ failure
ReactSmartStoreTests.testGetDatabaseSize()❌ failure
ReactSmartStoreTests.testGetRemoveGlobalStores()❌ failure
ReactSmartStoreTests.testGetRemoveStores()❌ failure
ReactSmartStoreTests.testGetSoupIndexSpecs()❌ failure
ReactSmartStoreTests.testMoveCursor()❌ failure
ReactSmartStoreTests.testQuerySoup()❌ failure
ReactSmartStoreTests.testRegisterExistsRemoveExists()❌ failure
ReactSmartStoreTests.testRemoveFromSoup()❌ failure
ReactSmartStoreTests.testSmartQuerySoup()❌ failure
ReactSmartStoreTests.testUpsertRetrieve()❌ failure

@github-actions

Copy link
Copy Markdown
TestsPassedSkippedFailed ❌️
iOS ^26 Test Results33 ran33 ❌
TestResult
iOS ^26 Test Results
ReactMobileSyncTests.testCleanResyncGhosts()❌ failure
ReactMobileSyncTests.testGetSyncStatusDeleteSync()❌ failure
ReactMobileSyncTests.testReSync()❌ failure
ReactMobileSyncTests.testSyncDown()❌ failure
ReactMobileSyncTests.testSyncUp()❌ failure
ReactNetTests.testCollectionCreateDeleteRetrieve()❌ failure
ReactNetTests.testCollectionCreateRetrieve()❌ failure
ReactNetTests.testCollectionUpsertUpdateRetrieve()❌ failure
ReactNetTests.testCreateDelRetrieve()❌ failure
ReactNetTests.testCreateRetrieve()❌ failure
ReactNetTests.testDescribe()❌ failure
ReactNetTests.testDescribeGlobal()❌ failure
ReactNetTests.testDescribeLayout()❌ failure
ReactNetTests.testGetApiVersion()❌ failure
ReactNetTests.testMetaData()❌ failure
ReactNetTests.testPublicApiCall()❌ failure
ReactNetTests.testQuery()❌ failure
ReactNetTests.testResources()❌ failure
ReactNetTests.testSearch()❌ failure
ReactNetTests.testUpsertUpdateRetrieve()❌ failure
ReactNetTests.testVersions()❌ failure
ReactOAuthTests.testGetAuthCredentials()❌ failure
ReactSmartStoreTests.testClearSoup()❌ failure
ReactSmartStoreTests.testGetDatabaseSize()❌ failure
ReactSmartStoreTests.testGetRemoveGlobalStores()❌ failure
ReactSmartStoreTests.testGetRemoveStores()❌ failure
ReactSmartStoreTests.testGetSoupIndexSpecs()❌ failure
ReactSmartStoreTests.testMoveCursor()❌ failure
ReactSmartStoreTests.testQuerySoup()❌ failure
ReactSmartStoreTests.testRegisterExistsRemoveExists()❌ failure
ReactSmartStoreTests.testRemoveFromSoup()❌ failure
ReactSmartStoreTests.testSmartQuerySoup()❌ failure
ReactSmartStoreTests.testUpsertRetrieve()❌ failure

@wmathurin

Copy link
Copy Markdown
Contributor Author

Changes are on the Android side - iOS failures are unrelated.

@wmathurin
wmathurin merged commit 157571f into forcedotcom:dev Jul 18, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants