You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[$175] [Sentry: APP-J60] Cannot read 'submitsTo' of null in getSuggestedSearchesVisibility #103774
A null policy or workflow object reaches getSuggestedSearchesVisibility while SearchQueryProvider renders. The code reads submitsTo on it without a null check.
Reproduction
Unknown. See the events linked from the Sentry issue.
changed the title [-][Sentry: APP-J60] Cannot read 'submitsTo' of null in getSuggestedSearchesVisibility[/-][+][$175] [Sentry: APP-J60] Cannot read 'submitsTo' of null in getSuggestedSearchesVisibility[/+]on Oct 12, 2026
In getSuggestedSearchesVisibility, the isSubmittedTo check iterates Object.values(policy.employeeList ?? {}) and reads employee.submitsTo / employee.forwardsTo without checking the entry. employeeList is a map of email to employee, and entries can be null (for example a member removed by an optimistic or server update that sets the key to null). A null entry then throws inside Array.some. The policy itself is already null-guarded just above, so the null is the employee entry, not the policy.
Check the other employeeList loops in this function (and isPolicyEligibleForTopSpenders) for the same unguarded access and apply the same optional chaining.
Add a unit test in tests/unit/Search/SearchUIUtilsTest.ts, e.g. "getSuggestedSearchesVisibility does not throw when employeeList contains a null entry". It passes a policy with employeeList: {'a@b.com': null} and expects no throw and a valid visibility result. The test should fail on current main.
What alternative solutions did you explore? (Optional)
Strip null employees when policies are read from Onyx: it touches many consumers and is a larger change than a local null check.
(Vit's agent) @suneox, can you review the proposal on this issue? There is no proposal from Melvin here yet, only the one above from a contributor. Please tell us which of these fits:
This is ready to open for contributors as it stands.
We should ask for expert contributor help, because it needs deeper native, navigation or FlashList knowledge.
policy.employeeList sometimes holds a null entry. getSuggestedSearchesVisibility reads employee.submitsTo on every entry without a null check, so it throws. The policy itself is already guarded at line 733. The sibling helper isPolicyApprover already uses employee?..
Add a unit test that passes employeeList: {'a@b.com': null} to getSuggestedSearchesVisibility and checks that it doesn't throw.
The existing proposal above covers steps 1 and 3.
What alternative solutions did you explore? (Optional)
Type PolicyEmployeeList as Record<string, PolicyEmployee | null | undefined>. TypeScript would then flag every unguarded read. This is a wider change, so it fits better as a follow-up.
Strip null entries when policies load from Onyx. This touches many consumers, and the exact source of the null isn't confirmed (see below).
Investigation details (where the null comes from is unconfirmed)
The app's own member-removal writes set employeeList: {[email]: null}, for example in Member.ts:414 and Member.ts:1233. They use MERGE, which normally removes nested nulls.
One possible gap, based on reading the code without reproducing it: when Onyx.update batches several policy keys and an earlier operation sets employeeList: null, the later employee list replaces the object without removing nulls. App code writes employeeList: null only on rare failure paths (Policy.ts:3093, Policy.ts:3746). The backend may also send this pattern.
PolicyEmployeeList is typed as Record<string, PolicyEmployee>, so TypeScript never flags these reads.
I couldn't pull Sentry event details for this run, so I couldn't confirm what users did just before the crash.
No related open issues found.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/103774) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
This doesn't need an expert contributor, it isn't related to native code, navigation, FlashList. It's a missing null check on the workspace member list
Possible causes in production:
From App code alone (rare): an admin is offline, creates a new workspace and removes a member from it. When the app reconnects, the workspace creation fails while the member removal succeeds, and both results are applied in the same batch.
Main suspect (not confirmed yet): data sent by the server. In Sentry, all events come from admins and most crashes happen right after the app reconnects and receives data for several workspaces at once.
So Melvin proposal is straightforward to fix the issue.
The PR skips null employeeList entries in getSuggestedSearchesVisibility and in the other loops from the proposal. It also guards two more loops in DynamicWorkspaceWorkflowsApprovalsExpensesFromPage.tsx that read entries the same way. The new unit test fails on main with a TypeError and passes with the fix.
Please re-state the problem that we are trying to solve in this issue.
When navigating or loading search suggestions, getSuggestedSearchesVisibility crashes with TypeError: Cannot read properties of null (reading 'submitsTo') when inspecting policy.employeeList containing null member entries (e.g., following optimistic member removal or deletion).
What is the root cause of that problem?
In src/libs/SearchUIUtils.ts, getSuggestedSearchesVisibility determines whether the current user is an approver by inspecting policy.employeeList:
While policy itself is guarded against null/undefined, entries in policy.employeeList can be null at runtime (for instance, when workspace members are removed or updated via optimistic Onyx actions such as Member.ts which set { [email]: null } during deletion).
When Object.values iterates over employeeList, it encounters a null entry. Directly accessing .submitsTo on null throws: TypeError: Cannot read properties of null (reading 'submitsTo').
Because SearchQueryProvider.tsx:66 executes getSuggestedSearchesVisibility across all workspace policies on every render, this exception unhandled at the top level causes SearchQueryProvider to crash, tearing down the authenticated screen tree.
What changes do you think we should make in order to solve the problem?
In src/libs/SearchUIUtils.ts inside getSuggestedSearchesVisibility, add optional chaining when accessing employee:
Apply the same defensive optional chaining employee?. across sibling unguarded accesses:
src/libs/WorkflowUtils.ts:827
src/libs/WorkflowUtils.ts:1847-1850
src/pages/workspace/workflows/approvals/DynamicWorkspaceWorkflowsApprovalsExpensesFromPage.tsx:124
This aligns with the pattern already used in src/libs/PolicyUtils/permissions.ts (isPolicyApprover).
Add a unit test in tests/unit/Search/SearchUIUtilsTest.ts:
Pass a mock policy with an employeeList containing a null entry (e.g. { 'removed@user.com': null }).
Verify that getSuggestedSearchesVisibility executes successfully without throwing and returns correct visibility booleans.
What alternative solutions did you explore? (Optional)
Sanitizing/stripping null entries in Onyx policy subscribers: Rejected because it adds overhead across all policy subscribers and touches global state rather than fixing the local property dereferencing.
Activity
melvin-bot commentedon Oct 12, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @suneox (
External)[-][Sentry: APP-J60] Cannot read 'submitsTo' of null in getSuggestedSearchesVisibility[/-][+][$175] [Sentry: APP-J60] Cannot read 'submitsTo' of null in getSuggestedSearchesVisibility[/+]melvin-bot commentedon Oct 12, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022109653480148614230
haiquywork commentedon Oct 12, 2026
Proposal
What is the root cause of that problem?
In
getSuggestedSearchesVisibility, theisSubmittedTocheck iteratesObject.values(policy.employeeList ?? {})and readsemployee.submitsTo/employee.forwardsTowithout checking the entry.employeeListis a map of email to employee, and entries can benull(for example a member removed by an optimistic or server update that sets the key to null). A null entry then throws insideArray.some. Thepolicyitself is already null-guarded just above, so the null is the employee entry, not the policy.App/src/libs/SearchUIUtils.ts
Lines 745 to 746 in 6502fec
SearchQueryProvidercalls this on every render with all policies, so the error crashes the whole Search page.App/src/components/Search/SearchQueryProvider.tsx
Line 66 in 6502fec
What changes do you think we should make in order to solve the problem?
getSuggestedSearchesVisibility(src/libs/SearchUIUtils.ts), make theisSubmittedTocallback null-safe:employeeListloops in this function (andisPolicyEligibleForTopSpenders) for the same unguarded access and apply the same optional chaining.employeeList: {'a@b.com': null}and expects no throw and a valid visibility result. The test should fail on current main.What alternative solutions did you explore? (Optional)
mountiny commentedon Oct 12, 2026
(Vit's agent) @suneox, can you review the proposal on this issue? There is no proposal from Melvin here yet, only the one above from a contributor. Please tell us which of these fits:
MelvinBot commentedon Oct 12, 2026
Proposal
What is the root cause of that problem?
policy.employeeListsometimes holds anullentry.getSuggestedSearchesVisibilityreadsemployee.submitsToon every entry without a null check, so it throws. The policy itself is already guarded at line 733. The sibling helperisPolicyApproveralready usesemployee?..The impact is large because
SearchQueryProvidercalls this function on every render. That provider wraps the whole signed-in navigator (AuthScreens.tsx:193), so one bad entry can break every screen, not only Search. Scope Top Spenders suggested search to eligible workspace policyIDs added this call (merged 2026-07-20, two days before Sentry first saw the error). Persist filters across view in Search brought it back after a revert.What changes do you think we should make in order to solve the problem?
getSuggestedSearchesVisibility, useemployee?.submitsToandemployee?.forwardsTo, matchingisPolicyApprover.employee?.guard to three other places that read entries the same unguarded way:WorkflowUtils.ts:827WorkflowUtils.ts:1847-1850DynamicWorkspaceWorkflowsApprovalsExpensesFromPage.tsx:124employeeList: {'a@b.com': null}togetSuggestedSearchesVisibilityand checks that it doesn't throw.The existing proposal above covers steps 1 and 3.
What alternative solutions did you explore? (Optional)
PolicyEmployeeListasRecord<string, PolicyEmployee | null | undefined>. TypeScript would then flag every unguarded read. This is a wider change, so it fits better as a follow-up.Investigation details (where the null comes from is unconfirmed)
employeeList: {[email]: null}, for example inMember.ts:414andMember.ts:1233. They use MERGE, which normally removes nested nulls.Onyx.updatebatches several policy keys and an earlier operation setsemployeeList: null, the later employee list replaces the object without removing nulls. App code writesemployeeList: nullonly on rare failure paths (Policy.ts:3093,Policy.ts:3746). The backend may also send this pattern.PolicyEmployeeListis typed asRecord<string, PolicyEmployee>, so TypeScript never flags these reads.Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/103774)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
suneox commentedon Oct 12, 2026
@haiquywork Please follow our contributor guideline do not post proposals on issues without the
Help Wantedlabel.suneox commentedon Oct 12, 2026
This doesn't need an expert contributor, it isn't related to native code, navigation, FlashList. It's a missing null check on the workspace member list
Possible causes in production:
So Melvin proposal is straightforward to fix the issue.
@MelvinBot let implement this
🎀 👀 🎀 C+ reviewed
melvin-bot commentedon Oct 12, 2026
Current assignee @mountiny is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.
MelvinBot commentedon Oct 12, 2026
🤖 Draft PR created: Guard against null workspace member entries in employeeList
Please mark it as "Ready for review" or ask me to do that for you when it is ready for review.
The PR skips null
employeeListentries ingetSuggestedSearchesVisibilityand in the other loops from the proposal. It also guards two more loops inDynamicWorkspaceWorkflowsApprovalsExpensesFromPage.tsxthat read entries the same way. The new unit test fails onmainwith aTypeErrorand passes with the fix.view run
camesenin commentedon Oct 13, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When navigating or loading search suggestions,
getSuggestedSearchesVisibilitycrashes withTypeError: Cannot read properties of null (reading 'submitsTo')when inspectingpolicy.employeeListcontaining null member entries (e.g., following optimistic member removal or deletion).What is the root cause of that problem?
In
src/libs/SearchUIUtils.ts,getSuggestedSearchesVisibilitydetermines whether the current user is an approver by inspectingpolicy.employeeList:While
policyitself is guarded against null/undefined, entries inpolicy.employeeListcan benullat runtime (for instance, when workspace members are removed or updated via optimistic Onyx actions such asMember.tswhich set{ [email]: null }during deletion).When
Object.valuesiterates overemployeeList, it encounters anullentry. Directly accessing.submitsToonnullthrows:TypeError: Cannot read properties of null (reading 'submitsTo').Because
SearchQueryProvider.tsx:66executesgetSuggestedSearchesVisibilityacross all workspace policies on every render, this exception unhandled at the top level causesSearchQueryProviderto crash, tearing down the authenticated screen tree.What changes do you think we should make in order to solve the problem?
src/libs/SearchUIUtils.tsinsidegetSuggestedSearchesVisibility, add optional chaining when accessingemployee:employee?.across sibling unguarded accesses:src/libs/WorkflowUtils.ts:827src/libs/WorkflowUtils.ts:1847-1850src/pages/workspace/workflows/approvals/DynamicWorkspaceWorkflowsApprovalsExpensesFromPage.tsx:124This aligns with the pattern already used in
src/libs/PolicyUtils/permissions.ts(isPolicyApprover).tests/unit/Search/SearchUIUtilsTest.ts:employeeListcontaining a null entry (e.g.{ 'removed@user.com': null }).getSuggestedSearchesVisibilityexecutes successfully without throwing and returns correct visibility booleans.What alternative solutions did you explore? (Optional)
Onyxpolicy subscribers: Rejected because it adds overhead across all policy subscribers and touches global state rather than fixing the local property dereferencing.Contributor details
Your Expensify account email: camesenin@gmail.com
Upwork Profile link: https://www.upwork.com/freelancers/~0170d6b02e9440e604
melvin-bot commentedon Oct 13, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
mountiny commentedon Oct 13, 2026
@suneox feel free to go ahead