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-C2W] Web: Copy in the report action context menu crashes when the report action is null #104746
The copy to clipboard action reads reportAction?.actionName in every branch of its else if chain except one. On current main this is ContextMenuActions.tsx:1200: isTagModificationAction(reportAction.actionName). When the context menu runs with a null report action, that branch throws.
Reproduction
Unknown. See the Sentry events linked from the Sentry issue. The crash happens when the user presses Copy in the report action context menu.
changed the title [-][Sentry: APP-C2W] Web: Copy in the report action context menu crashes when the report action is null[/-][+][$175] [Sentry: APP-C2W] Web: Copy in the report action context menu crashes when the report action is null[/+]on Oct 22, 2026
Please re-state the problem that we are trying to solve in this issue.
On web, pressing Copy in the report action context menu can throw TypeError: Cannot read properties of null (reading 'actionName'). The crash is in the "Copy to clipboard" onPress in ContextMenuActions.tsx. It happens when the menu runs with a null/undefinedreportAction. The exception is uncaught and surfaces through global_handlers.onerror. Sentry shows 24 users and 88 events, and it is still ongoing. Copy should never crash. With no report action it should fall through to the normal fallback copy behavior.
What is the root cause of that problem?
The onPress for the copy action receives reportAction typed as an optional/nullable Onyx entry. Every branch of its else if chain reads reportAction?.actionName defensively except one:
If the underlying action is gone by the time the user presses Copy, this branch dereferences null. This can happen when an optimistic action is replaced or removed in Onyx while the menu is still open. Because the earlier branches are optional-chained, evaluation reaches this unguarded one and throws. The missing ?. is a plain inconsistency, so it needs no deeper state fix.
What changes do you think we should make in order to solve the problem?
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx, in the Copy to clipboard onPress, guard the one unsafe branch:
Using reportAction && rather than reportAction?.actionName has two benefits:
It keeps isTagModificationAction's parameter type as a non-optional string, so no signature change is needed.
It narrows reportAction to non-null inside the block, so any reportAction usage in the body (e.g. passing it to helpers) type-checks without ! or extra ?..
When reportAction is null, the branch is skipped and evaluation continues to the remaining branches and the final else. Those already handle a missing action, so the user gets the default copy behavior (copy the displayed text/HTML) instead of a crash.
While there, audit the rest of that onPress and the neighboring context-menu actions (BaseReportActionContextMenu.tsx callback path) for any other bare reportAction. dereferences, and make them consistent. No behavior change is expected for non-null actions.
What alternative solutions did you explore? (Optional)
Early return when reportAction is null: This would stop the crash, but it would silently disable copying for cases that currently work, such as copying selected text or a plain-text message via the fallback branch. Rejected as a regression risk.
Hide/disable the Copy item when reportAction is null: This is a larger UX and logic change in shouldShow, and it would not protect against the action disappearing between render and press. Rejected as unnecessary.
Wrap onPress in try/catch: This hides the bug instead of fixing it. Rejected.
reportAction?.actionName with a widened isTagModificationAction signature: This works but spreads nullability into a utility function for no benefit. Rejected.
Testing
Unit test (e.g. tests/unit/ContextMenuActionsTest.ts):
Find the copyToClipboard entry in ContextMenuActions.
Call its onPress with reportAction: undefined and again with null (cast as needed). Use a mocked Clipboard.setString and closePopover = true. Assert that it does not throw and falls through to the default copy behavior.
Call onPress with a real tag-modification report action, e.g. UPDATE_TAG / ADD_TAG. Assert Clipboard.setString is called with the expected tag message, to confirm the existing behavior is unchanged.
Manual (web):
Open a report with messages and right-click a message to open the context menu.
Trigger removal of the underlying action while the menu is open, for example by deleting the message from another client, or by using an optimistic action that gets replaced.
Click Copy and verify there is no console error or Sentry event.
Verify normal copying still works for text messages, tag-change actions, and other action types.
Regression: run npm run typecheck, npm run lint, and the context menu unit tests.
Please re-state the problem that we are trying to solve in this issue.
On web, pressing Copy in a report action context menu crashes with Cannot read properties of null (reading 'actionName') when the menu has no report action.
What is the root cause of that problem?
The context menu can open, or stay open, while its report action lookup returns nothing. Copy still shows, and its press handler assumes the action exists.
The menu looks up the action in Onyx by ID. The lookup returns undefined when the ID is cleared or the action is not in the report's actions (BaseReportActionContextMenu.tsx:180-185).
Copy's shouldShow returns true for a missing action, because every check in it returns false for undefined (ContextMenuActions.tsx:1048-1053).
In onPress, every check before line 1200 handles null. Line 1200 reads reportAction.actionName without ?. and throws (ContextMenuActions.tsx:1200). Lines 1224-1282 have the same pattern, so fixing only line 1200 moves the crash to line 1224.
How the action likely becomes null
Hide/show race (most likely on web).runAndResetOnPopoverHide clears the action ID, report ID and selection refs when the popover hides, without checking whether a new menu is already open (PopoverReportActionContextMenu.tsx:285-294). If you right-click message B while menu A is still closing, A's hide callback can wipe B's refs. The next re-render passes no action ID and an empty selection. An empty selection also explains why the crash skips the selection branch at line 1101.
The action is removed from Onyx while the menu is open, for example when an optimistic action is replaced.
The original report ID doesn't contain the action in some combined views, so the lookup is empty from the first render.
I have not confirmed which trigger is most common in production. The Sentry breadcrumbs would show it.
What changes do you think we should make in order to solve the problem?
In Copy's onPress, keep the selection branch first. Then return early if reportAction is missing, before the action-type checks.
Hide Copy when there is no report action by adding a reportAction check to its shouldShow.
Change ContextMenuActionPayload.reportAction to OnyxEntry<ReportAction> and remove the cast in BaseReportActionContextMenu.tsx. TypeScript will then flag every unsafe access, including lines 1224-1282.
Apply the same guard to Reply in thread. It has the same gap: its shouldShow passes with no action, and its onPress reads reportAction.actorAccountID (ContextMenuActions.tsx:545).
Add a unit test: given a REPORT_ACTION menu with no action, Copy is hidden, and pressing it does not throw.
What alternative solutions did you explore? (Optional)
Only add ?. at line 1200. This is not enough because line 1224 throws next.
Fix the popover race too. Make runAndResetOnPopoverHide skip clearing refs when a newer menu is visible, for example by checking an instance token. This fixes one trigger, but the guard above is still needed for the Onyx-removal case.
Code history
The unsafe lines are old, so this looks like a long-standing runtime race rather than a recent regression. The UPDATE_FEATURE_ENABLED branch at line 1224 came from [Policy Change Logs] Add policy changelog for "enabled/disabled <feature>". The null as ReportAction cast and line 1200 are older than June 2025.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/104746) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Please re-state the problem that we are trying to solve in this issue.
On Web, clicking Copy to clipboard in the report action context menu can crash the entire application with TypeError: Cannot read properties of null (reading 'actionName') if reportAction is null or undefined (e.g. if the action was optimistically deleted, dismissed, or unloaded while the menu was open).
What is the root cause of that problem?
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:
In the onPress callback for CONST.CONTEXT_MENU_ACTIONS.COPY_TO_CLIPBOARD (around lines 1180–1240), there is an if ... else if chain checking reportAction?.actionName across different policy change log action types.
All branches safely use optional chaining (reportAction?.actionName === ...), except line 1200:
Line 1200 directly dereferences reportAction.actionName without checking if reportAction is null. When reportAction is null/undefined, evaluating this condition immediately throws an unhandled TypeError.
What changes do you think we should make in order to solve the problem?
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:
Guard the isTagModificationAction condition by ensuring reportAction exists before accessing .actionName:
Preserves Type Safety: Narrows reportAction to non-null within the branch body, allowing getWorkspaceTagUpdateMessage(translate, reportAction) and isTagModificationAction(reportAction.actionName) to typecheck cleanly without needing non-null assertions (!) or changing utility signatures.
Graceful Fallback: If reportAction is null, it cleanly passes down to subsequent branches and the default clipboard fallback (e.g. copying selected text/selection).
Targeted & Minimal: Single-line fix that matches the defensive pattern used across the rest of ContextMenuActions.tsx.
What alternative solutions did you explore? (Optional)
Changing isTagModificationAction(actionName?: string): boolean in ReportActionsUtils.ts:
Unnecessary widening of a pure utility function that expects a valid string action name across many caller sites. Checking reportAction && at the callsite is cleaner and safer.
Early return if !reportAction:
Avoided because the context menu copy handler also supports copying user selection text even when there is no specific action attached.
What automated tests can you add to prevent this from re-occurring?
In tests/unit/ContextMenuActionsTest.ts:
Invoke the COPY_TO_CLIPBOARDonPress callback with reportAction: null and verify that no error is thrown and the clipboard fallback is executed gracefully.
Invoke with a valid tag modification action (e.g. CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_TAG) and verify that getWorkspaceTagUpdateMessage is correctly formatted and copied.
Please re-state the problem that we are trying to solve in this issue.
On Web, clicking Copy to clipboard in the report action context menu crashes the application with TypeError: Cannot read properties of null (reading 'actionName') when reportAction is null or undefined (e.g. if the action was optimistically replaced/unloaded in Onyx while the popover was visible, or triggered during a popover hide/reset race).
What is the root cause of that problem?
Context Menu Runtime Race & Empty Action Payload:
In BaseReportActionContextMenu.tsx:481, when the user selects a context menu item, it calls:
If the report action was optimistically deleted or replaced in Onyx while the popover was active, or if runAndResetOnPopoverHide (PopoverReportActionContextMenu.tsx:285–294) cleared the action ID ref during a popover transition before the click handler executed, reportAction is passed as null or undefined.
Un-guarded Accesses in the 500-Line Action Type Branching:
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:1091:
If selection is present, it copies the highlighted text and returns safely.
However, if selection is empty (""), execution falls through into the else if chain of action-specific copy formatting.
Because reportAction is null, evaluating reportAction.actionName immediately throws: TypeError: Cannot read properties of null (reading 'actionName').
Why Single-Line Competitor Fixes are Fatally Flawed:
A competitor proposed only adding reportAction && at line 1187 (isTagModificationAction). This is completely broken. Immediately following that branch (lines 1205 to 1238), there are 17 consecutive branches that directly access reportAction.actionName without optional chaining (e.g. reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_FEATURE_ENABLED, UPDATE_IS_ATTENDEE_TRACKING_ENABLED, UPDATE_DEFAULT_APPROVER, UPDATE_SUBMITS_TO, etc.). Simply patching line 1187 will cause the very next branch at line 1205 to throw the identical TypeError.
What changes do you think we should make in order to solve the problem?
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:
Short-Circuit Empty Actions in COPY_TO_CLIPBOARDonPress:
After handling if (selection), immediately return early if !reportAction. If there is no highlighted selection and no report action, there is nothing to copy. This cleanly bypasses the entire 500-line action-specific branch chain and guarantees type-safe non-null reportAction across all subsequent branches:
--- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx+++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx@@ -1093,6 +1093,8 @@ const getContextMenuActions = (
if (selection) {
// When the user has highlighted part of a message, always copy exactly what's selected,
// regardless of the report action type (including system messages like "marked as paid").
setClipboardMessageWithCleanedMentions(selection, reportIDToName);
+ } else if (!reportAction) {+ return;
} else if (isReportPreviewAction) {
const iouReportID = getIOUReportIDFromReportActionPreview(reportAction);
Add Guard to shouldShow:
Ensure Copy is suppressed when there is neither an active report action nor a highlighted selection:
100% Comprehensive: Fully protects lines 1187, 1205–1238, and all subsequent branches without needing to patch 20+ separate lines.
Preserves Text Selection Copying: If a user highlighted text before the action unloaded or transitioned, the highlighted text is still copied cleanly via selection.
Zero Overhead & Zero Breaking Changes: Avoids large type rewrites or breaking signature changes across BaseReportActionContextMenu.tsx.
What alternative solutions did you explore? (Optional)
Only adding reportAction && to isTagModificationAction (Competitor proposal):
Rejected because lines 1205 to 1238 immediately dereference reportAction.actionName across 17 other branches. The crash would simply shift 5 lines down.
Hiding Copy in shouldShow without an onPress guard (MelvinBot proposal):
Rejected because shouldShow is evaluated when the menu renders. If an action is removed or replaced in Onyx after the menu is open, the click handler still fires and would crash without the defensive onPress short-circuit.
What specific scenarios should we cover in automated tests to prevent reintroduction of this issue in the future?
In tests/unit/ContextMenuActionsCopyMessageTest.ts:
Null reportAction with Empty Selection:
Call copyMessageAction.onPress(false, createReportActionPayload(null)) where selection: ''.
Assert that it executes cleanly without throwing an unhandled TypeError and does not call Clipboard.setString or setHtml.
Null reportAction with Non-Empty Selection:
Call copyMessageAction.onPress(false, createPayload('Hello world')) with reportAction: null.
Assert that the highlighted text is successfully copied to the clipboard.
Valid Report Action Copy:
Verify existing tests continue to pass for task, modified expense, and policy change log actions.
Please re-state the problem that we are trying to solve in this issue.
On Web, clicking Copy in the report action context menu can throw an unhandled TypeError: Cannot read properties of null (reading 'actionName') when reportAction is null or undefined (e.g. when an action was optimistically deleted or unloaded in Onyx while the context menu was open, or during popover dismissal/re-open race conditions).
What is the root cause of that problem?
The crash stems from a combination of missing shouldShow validation, an unsafe type cast, and un-guarded property dereferencing in the COPY_TO_CLIPBOARD action:
Unsafe Non-Null Assertion in BaseReportActionContextMenu.tsx:407:
In BaseReportActionContextMenu.tsx:
When reportAction is undefined or null, it is cast to a non-nullable ReportAction. This masks nullable runtime states from TypeScript across ContextMenuActions.tsx.
Missing Guard in copyMessage.shouldShow (ContextMenuActions.tsx:1048-1053):
All four negation helpers evaluate to true when reportAction is null or undefined (e.g. !isReportActionAttachment(undefined) === true). Because !!reportAction is missing, the Copy action is rendered even when no report action exists.
Cascading Unguarded Property Access in onPress (ContextMenuActions.tsx:1099-1282):
When selection is empty (the user right-clicked without highlighting text), execution passes into the action-type else if chain:
Line 1200 dereferences reportAction.actionName directly without optional chaining:
Crucially, even if line 1200 is patched with optional chaining, lines 1224 to 1270 contain 17 consecutive branches that directly access reportAction.actionName without optional chaining (e.g. line 1224: reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_FEATURE_ENABLED).
Thus, any single-line patch at line 1200 simply shifts the fatal TypeError down to line 1224.
What changes do you think we should make in order to solve the problem?
Short-Circuit Null reportAction in copyMessage.onPress (src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx):
After handling if (selection) (which correctly preserves text copying when the user has highlighted text), immediately return if !reportAction. This completely bypasses the entire 500-line action-specific branch chain and allows TypeScript control-flow analysis to narrow reportAction to non-null for all subsequent branches:
--- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx+++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx@@ -1103,6 +1103,8 @@ const getContextMenuActions = (
// regardless of the report action type (including system messages like "marked as paid").
setClipboardMessageWithCleanedMentions(selection, reportIDToName);
+ } else if (!reportAction) {+ return;
} else if (isReportPreviewAction) {
const iouReportID = getIOUReportIDFromReportActionPreview(reportAction);
const displayMessage = getReportPreviewMessageForCopy({
Add !!reportAction to copyMessage.shouldShow (src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx):
Prevent the Copy item from rendering when reportAction is missing for REPORT_ACTION context menu types:
What alternative solutions did you explore? (Optional)
Only adding reportAction && to isTagModificationAction (as proposed in earlier comments):
Incomplete. Lines 1224–1270 immediately access reportAction.actionName across 17 other branches. Fixing line 1200 alone moves the crash to line 1224.
Only adding !!reportAction to shouldShow:
Incomplete. If an action is removed or replaced in Onyx after the menu is opened, or if a hide/open race occurs, onPress can still be invoked with a null reportAction. Defense-in-depth in onPress is strictly required.
Returning early at the very start of onPress before if (selection):
Rejected because copying highlighted text (selection) must continue to work even if the action reference is detached or transitioning.
What specific scenarios should we cover in automated tests to prevent reintroduction of this issue in the future?
In tests/unit/ContextMenuActionsCopyMessageTest.ts:
Empty Selection with Null reportAction:
Call copyMessageAction.onPress(false, createReportActionPayload(null as unknown as ReportAction)).
Assert that execution completes without throwing a TypeError and that Clipboard.setString and Clipboard.setHtml are not called.
Text Selection with Null reportAction:
Call copyMessageAction.onPress(false, createPayload('Sample highlighted text')) with reportAction: null as unknown as ReportAction.
Assert that Clipboard.setString / Clipboard.setHtml is called with the cleaned highlighted text.
shouldShow with Null reportAction:
Assert that copyMessageAction.shouldShow({type: CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, reportAction: null}) returns false.
On Web, pressing Copy in the report action context menu crashes with TypeError: Cannot read properties of null (reading 'actionName') when reportAction is null or undefined (e.g. if the action is detached, removed, or replaced in Onyx while the context menu is open). Sentry issue APP-C2W.
Root Cause
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx, the onPress callback for reportActionContextMenu.copyMessage handles multiple action types in an if...else if chain.
In lines 1100–1104, when selection is present (the user highlighted text), it safely calls setClipboardMessageWithCleanedMentions(selection, reportIDToName).
However, when selection is empty:
Line 1200 unguardedly reads reportAction.actionName:
If reportAction is nullish, evaluating these branches immediately throws Cannot read properties of null (reading 'actionName').
Additionally, copyMessage's shouldShow in ContextMenuActions.tsx:1048-1053 checks !isReportActionAttachment(reportAction) and !isMessageDeleted(reportAction) without checking !!reportAction first, allowing the Copy item to show even if reportAction is nullish. And ContextMenuActionPayload types reportAction: ReportAction instead of OnyxEntry<ReportAction>, hiding nullability at the type level.
Solution
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:
In reportActionContextMenu.copyMessage's onPress: after handling if (selection), immediately return early if !reportAction:
This completely protects all subsequent branches (including lines 1200 and 1224–1282) from null dereferences while preserving the ability to copy highlighted text.
Adding reportAction && solely to line 1200 (isTagModificationAction): Incomplete. It would still crash on line 1224 (UPDATE_FEATURE_ENABLED) and subsequent branches when none of the earlier branches match.
Returning early before if (selection): Rejected because copying user-highlighted text should remain functional even if reportAction is nullish or detaching.
To comprehensively protect not only line 1200 (isTagModificationAction) but also all subsequent branches (lines 1224–1282, which also access reportAction.actionName === ...), the most robust approach is:
In src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:
In copyMessage's onPress, immediately return early if !reportAction after handling user text selection:
Accurately type reportAction: OnyxEntry<ReportAction> in ContextMenuActionPayload and adjust BaseReportActionContextMenu.tsx accordingly so TypeScript catches any future unguarded accesses at compile time.
We added a new check using reportAction.actionName in the onPress handler without a null check. This can cause a TypeError: Cannot read properties of null (reading 'actionName') when reportAction is null and one of these branches executes.
I suggest using reportAction?.actionName here, consistent with the existing null checks. There's no need to hide the Copy action when reportAction is null. We just need to handle the null case safely.
Agreed, huult. Adding ?. alone fixes the crash, and I'm fine dropping the other parts of my proposal (hiding Copy, changing the payload type, the Reply in thread guard, and the unit test).
I checked the whole Copy onPress chain at ba97c03. These are the only places that read reportAction.actionName without ?.:
Every other branch either uses reportAction?.actionName or calls a helper such as isActionOfType that returns false for a missing action.
The other bare reads, like reportAction.actorAccountID at line 1161 and line 1305, run only after a type check passes, so they can't see null.
After the fix, line 1200 passes undefined to isTagModificationAction. That's safe because the function only does === comparisons (ReportActionsUtils.ts:2092).
With no action and no selection, the chain reaches the content and messageText fallbacks at lines 1589-1594. Both are empty, so Copy copies nothing and closes the menu without crashing. A user who selected text still gets it copied at line 1101.
To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/104746#issuecomment-6355600549) to create a draft PR that adds ?. on line 1200 and lines 1224-1282.
Activity
melvin-bot commentedon Oct 22, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @huult (
External)[-][Sentry: APP-C2W] Web: Copy in the report action context menu crashes when the report action is null[/-][+][$175] [Sentry: APP-C2W] Web: Copy in the report action context menu crashes when the report action is null[/+]melvin-bot commentedon Oct 22, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022113270966151413998
Abdallah-1551 commentedon Oct 22, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On web, pressing Copy in the report action context menu can throw
TypeError: Cannot read properties of null (reading 'actionName'). The crash is in the "Copy to clipboard"onPressinContextMenuActions.tsx. It happens when the menu runs with anull/undefinedreportAction. The exception is uncaught and surfaces throughglobal_handlers.onerror. Sentry shows 24 users and 88 events, and it is still ongoing. Copy should never crash. With no report action it should fall through to the normal fallback copy behavior.What is the root cause of that problem?
The
onPressfor the copy action receivesreportActiontyped as an optional/nullable Onyx entry. Every branch of itselse ifchain readsreportAction?.actionNamedefensively except one:If the underlying action is gone by the time the user presses Copy, this branch dereferences
null. This can happen when an optimistic action is replaced or removed in Onyx while the menu is still open. Because the earlier branches are optional-chained, evaluation reaches this unguarded one and throws. The missing?.is a plain inconsistency, so it needs no deeper state fix.What changes do you think we should make in order to solve the problem?
In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx, in the Copy to clipboardonPress, guard the one unsafe branch:Using
reportAction &&rather thanreportAction?.actionNamehas two benefits:isTagModificationAction's parameter type as a non-optionalstring, so no signature change is needed.reportActionto non-null inside the block, so anyreportActionusage in the body (e.g. passing it to helpers) type-checks without!or extra?..When
reportActionis null, the branch is skipped and evaluation continues to the remaining branches and the finalelse. Those already handle a missing action, so the user gets the default copy behavior (copy the displayed text/HTML) instead of a crash.While there, audit the rest of that
onPressand the neighboring context-menu actions (BaseReportActionContextMenu.tsxcallback path) for any other barereportAction.dereferences, and make them consistent. No behavior change is expected for non-null actions.What alternative solutions did you explore? (Optional)
returnwhenreportActionis null: This would stop the crash, but it would silently disable copying for cases that currently work, such as copying selected text or a plain-text message via the fallback branch. Rejected as a regression risk.reportActionis null: This is a larger UX and logic change inshouldShow, and it would not protect against the action disappearing between render and press. Rejected as unnecessary.onPressin try/catch: This hides the bug instead of fixing it. Rejected.reportAction?.actionNamewith a widenedisTagModificationActionsignature: This works but spreads nullability into a utility function for no benefit. Rejected.Testing
Unit test (e.g.
tests/unit/ContextMenuActionsTest.ts):copyToClipboardentry inContextMenuActions.onPresswithreportAction: undefinedand again withnull(cast as needed). Use a mockedClipboard.setStringandclosePopover = true. Assert that it does not throw and falls through to the default copy behavior.onPresswith a real tag-modification report action, e.g.UPDATE_TAG/ADD_TAG. AssertClipboard.setStringis called with the expected tag message, to confirm the existing behavior is unchanged.Manual (web):
Regression: run
npm run typecheck,npm run lint, and the context menu unit tests.MelvinBot commentedon Oct 22, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On web, pressing Copy in a report action context menu crashes with
Cannot read properties of null (reading 'actionName')when the menu has no report action.What is the root cause of that problem?
The context menu can open, or stay open, while its report action lookup returns nothing. Copy still shows, and its press handler assumes the action exists.
undefinedwhen the ID is cleared or the action is not in the report's actions (BaseReportActionContextMenu.tsx:180-185).onPressturns this intonulland casts it to a non-nullReportAction(BaseReportActionContextMenu.tsx:407). TypeScript can't see the null (ContextMenuActions.tsx:346).shouldShowreturns true for a missing action, because every check in it returns false forundefined(ContextMenuActions.tsx:1048-1053).onPress, every check before line 1200 handles null. Line 1200 readsreportAction.actionNamewithout?.and throws (ContextMenuActions.tsx:1200). Lines 1224-1282 have the same pattern, so fixing only line 1200 moves the crash to line 1224.How the action likely becomes null
runAndResetOnPopoverHideclears the action ID, report ID and selection refs when the popover hides, without checking whether a new menu is already open (PopoverReportActionContextMenu.tsx:285-294). If you right-click message B while menu A is still closing, A's hide callback can wipe B's refs. The next re-render passes no action ID and an empty selection. An empty selection also explains why the crash skips the selection branch at line 1101.I have not confirmed which trigger is most common in production. The Sentry breadcrumbs would show it.
What changes do you think we should make in order to solve the problem?
onPress, keep the selection branch first. Then return early ifreportActionis missing, before the action-type checks.reportActioncheck to itsshouldShow.ContextMenuActionPayload.reportActiontoOnyxEntry<ReportAction>and remove the cast inBaseReportActionContextMenu.tsx. TypeScript will then flag every unsafe access, including lines 1224-1282.shouldShowpasses with no action, and itsonPressreadsreportAction.actorAccountID(ContextMenuActions.tsx:545).What alternative solutions did you explore? (Optional)
?.at line 1200. This is not enough because line 1224 throws next.runAndResetOnPopoverHideskip clearing refs when a newer menu is visible, for example by checking an instance token. This fixes one trigger, but the guard above is still needed for the Onyx-removal case.Code history
The unsafe lines are old, so this looks like a long-standing runtime race rather than a recent regression. The
UPDATE_FEATURE_ENABLEDbranch at line 1224 came from [Policy Change Logs] Add policy changelog for "enabled/disabled <feature>". Thenull as ReportActioncast and line 1200 are older than June 2025.Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/104746)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
magomedta commentedon Oct 22, 2026
Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/~01777d047a06f0e470
Proposal
Please re-state the problem that we are trying to solve in this issue.
On Web, clicking Copy to clipboard in the report action context menu can crash the entire application with
TypeError: Cannot read properties of null (reading 'actionName')ifreportActionis null or undefined (e.g. if the action was optimistically deleted, dismissed, or unloaded while the menu was open).What is the root cause of that problem?
In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:In the
onPresscallback forCONST.CONTEXT_MENU_ACTIONS.COPY_TO_CLIPBOARD(around lines 1180–1240), there is anif ... else ifchain checkingreportAction?.actionNameacross different policy change log action types.All branches safely use optional chaining (
reportAction?.actionName === ...), except line 1200:Line 1200 directly dereferences
reportAction.actionNamewithout checking ifreportActionis null. WhenreportActionis null/undefined, evaluating this condition immediately throws an unhandledTypeError.What changes do you think we should make in order to solve the problem?
In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:Guard the
isTagModificationActioncondition by ensuringreportActionexists before accessing.actionName:Key Advantages:
reportActionto non-null within the branch body, allowinggetWorkspaceTagUpdateMessage(translate, reportAction)andisTagModificationAction(reportAction.actionName)to typecheck cleanly without needing non-null assertions (!) or changing utility signatures.reportActionis null, it cleanly passes down to subsequent branches and the default clipboard fallback (e.g. copying selected text/selection).ContextMenuActions.tsx.What alternative solutions did you explore? (Optional)
isTagModificationAction(actionName?: string): booleaninReportActionsUtils.ts:Unnecessary widening of a pure utility function that expects a valid string action name across many caller sites. Checking
reportAction &&at the callsite is cleaner and safer.!reportAction:Avoided because the context menu copy handler also supports copying user selection text even when there is no specific action attached.
What automated tests can you add to prevent this from re-occurring?
In
tests/unit/ContextMenuActionsTest.ts:COPY_TO_CLIPBOARDonPresscallback withreportAction: nulland verify that no error is thrown and the clipboard fallback is executed gracefully.CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_TAG) and verify thatgetWorkspaceTagUpdateMessageis correctly formatted and copied.melvin-bot commentedon Oct 22, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
sijo0218 commentedon Oct 22, 2026
Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/~011091133bbb09cd1c
Proposal
Please re-state the problem that we are trying to solve in this issue.
On Web, clicking Copy to clipboard in the report action context menu crashes the application with
TypeError: Cannot read properties of null (reading 'actionName')whenreportActionis null or undefined (e.g. if the action was optimistically replaced/unloaded in Onyx while the popover was visible, or triggered during a popover hide/reset race).What is the root cause of that problem?
Context Menu Runtime Race & Empty Action Payload:
In
BaseReportActionContextMenu.tsx:481, when the user selects a context menu item, it calls:If the report action was optimistically deleted or replaced in Onyx while the popover was active, or if
runAndResetOnPopoverHide(PopoverReportActionContextMenu.tsx:285–294) cleared the action ID ref during a popover transition before the click handler executed,reportActionis passed asnullorundefined.Un-guarded Accesses in the 500-Line Action Type Branching:
In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:1091:selectionis present, it copies the highlighted text and returns safely.selectionis empty (""), execution falls through into theelse ifchain of action-specific copy formatting.reportActionis null, evaluatingreportAction.actionNameimmediately throws:TypeError: Cannot read properties of null (reading 'actionName').Why Single-Line Competitor Fixes are Fatally Flawed:
A competitor proposed only adding
reportAction &&at line 1187 (isTagModificationAction). This is completely broken. Immediately following that branch (lines 1205 to 1238), there are 17 consecutive branches that directly accessreportAction.actionNamewithout optional chaining (e.g.reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_FEATURE_ENABLED,UPDATE_IS_ATTENDEE_TRACKING_ENABLED,UPDATE_DEFAULT_APPROVER,UPDATE_SUBMITS_TO, etc.). Simply patching line 1187 will cause the very next branch at line 1205 to throw the identicalTypeError.What changes do you think we should make in order to solve the problem?
In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:Short-Circuit Empty Actions in
COPY_TO_CLIPBOARDonPress:After handling
if (selection), immediately return early if!reportAction. If there is no highlighted selection and no report action, there is nothing to copy. This cleanly bypasses the entire 500-line action-specific branch chain and guarantees type-safe non-nullreportActionacross all subsequent branches:Add Guard to
shouldShow:Ensure Copy is suppressed when there is neither an active report action nor a highlighted selection:
Advantages of this Solution:
selection.BaseReportActionContextMenu.tsx.What alternative solutions did you explore? (Optional)
reportAction &&toisTagModificationAction(Competitor proposal):reportAction.actionNameacross 17 other branches. The crash would simply shift 5 lines down.shouldShowwithout anonPressguard (MelvinBot proposal):shouldShowis evaluated when the menu renders. If an action is removed or replaced in Onyx after the menu is open, the click handler still fires and would crash without the defensiveonPressshort-circuit.What specific scenarios should we cover in automated tests to prevent reintroduction of this issue in the future?
In
tests/unit/ContextMenuActionsCopyMessageTest.ts:reportActionwith Empty Selection:copyMessageAction.onPress(false, createReportActionPayload(null))whereselection: ''.TypeErrorand does not callClipboard.setStringorsetHtml.reportActionwith Non-Empty Selection:copyMessageAction.onPress(false, createPayload('Hello world'))withreportAction: null.melvin-bot commentedon Oct 22, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
HamzaKhanBUIC commentedon Oct 22, 2026
Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/hamzakhanbuic
Proposal
Please re-state the problem that we are trying to solve in this issue.
On Web, clicking Copy in the report action context menu can throw an unhandled
TypeError: Cannot read properties of null (reading 'actionName')whenreportActionis null or undefined (e.g. when an action was optimistically deleted or unloaded in Onyx while the context menu was open, or during popover dismissal/re-open race conditions).What is the root cause of that problem?
The crash stems from a combination of missing
shouldShowvalidation, an unsafe type cast, and un-guarded property dereferencing in theCOPY_TO_CLIPBOARDaction:Unsafe Non-Null Assertion in
BaseReportActionContextMenu.tsx:407:In
BaseReportActionContextMenu.tsx:When
reportActionis undefined or null, it is cast to a non-nullableReportAction. This masks nullable runtime states from TypeScript acrossContextMenuActions.tsx.Missing Guard in
copyMessage.shouldShow(ContextMenuActions.tsx:1048-1053):All four negation helpers evaluate to
truewhenreportActionis null or undefined (e.g.!isReportActionAttachment(undefined) === true). Because!!reportActionis missing, the Copy action is rendered even when no report action exists.Cascading Unguarded Property Access in
onPress(ContextMenuActions.tsx:1099-1282):When
selectionis empty (the user right-clicked without highlighting text), execution passes into the action-typeelse ifchain:reportAction.actionNamedirectly without optional chaining:reportAction.actionNamewithout optional chaining (e.g. line 1224:reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_FEATURE_ENABLED).TypeErrordown to line 1224.What changes do you think we should make in order to solve the problem?
Short-Circuit Null
reportActionincopyMessage.onPress(src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx):After handling
if (selection)(which correctly preserves text copying when the user has highlighted text), immediately return if!reportAction. This completely bypasses the entire 500-line action-specific branch chain and allows TypeScript control-flow analysis to narrowreportActionto non-null for all subsequent branches:Add
!!reportActiontocopyMessage.shouldShow(src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx):Prevent the Copy item from rendering when
reportActionis missing forREPORT_ACTIONcontext menu types:Refine Type Definition in
ContextMenuActionPayload& Remove Unsafe Cast:In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:And in
src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx:407:What alternative solutions did you explore? (Optional)
reportAction &&toisTagModificationAction(as proposed in earlier comments):reportAction.actionNameacross 17 other branches. Fixing line 1200 alone moves the crash to line 1224.!!reportActiontoshouldShow:onPresscan still be invoked with a nullreportAction. Defense-in-depth inonPressis strictly required.onPressbeforeif (selection):selection) must continue to work even if the action reference is detached or transitioning.What specific scenarios should we cover in automated tests to prevent reintroduction of this issue in the future?
In
tests/unit/ContextMenuActionsCopyMessageTest.ts:reportAction:copyMessageAction.onPress(false, createReportActionPayload(null as unknown as ReportAction)).TypeErrorand thatClipboard.setStringandClipboard.setHtmlare not called.reportAction:copyMessageAction.onPress(false, createPayload('Sample highlighted text'))withreportAction: null as unknown as ReportAction.Clipboard.setString/Clipboard.setHtmlis called with the cleaned highlighted text.shouldShowwith NullreportAction:copyMessageAction.shouldShow({type: CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, reportAction: null})returnsfalse.melvin-bot commentedon Oct 22, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
HamzaKhanBUIC commentedon Oct 22, 2026
Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/~015055563b6090af39
melvin-bot commentedon Oct 22, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
camesenin commentedon Oct 22, 2026
Proposal
Problem
On Web, pressing Copy in the report action context menu crashes with
TypeError: Cannot read properties of null (reading 'actionName')whenreportActionis null or undefined (e.g. if the action is detached, removed, or replaced in Onyx while the context menu is open). Sentry issueAPP-C2W.Root Cause
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx, theonPresscallback forreportActionContextMenu.copyMessagehandles multiple action types in anif...else ifchain.selectionis present (the user highlighted text), it safely callssetClipboardMessageWithCleanedMentions(selection, reportIDToName).selectionis empty:reportAction.actionName:reportAction.actionName:reportActionis nullish, evaluating these branches immediately throwsCannot read properties of null (reading 'actionName').copyMessage'sshouldShowinContextMenuActions.tsx:1048-1053checks!isReportActionAttachment(reportAction)and!isMessageDeleted(reportAction)without checking!!reportActionfirst, allowing the Copy item to show even ifreportActionis nullish. AndContextMenuActionPayloadtypesreportAction: ReportActioninstead ofOnyxEntry<ReportAction>, hiding nullability at the type level.Solution
In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:reportActionContextMenu.copyMessage'sonPress: after handlingif (selection), immediately return early if!reportAction:This completely protects all subsequent branches (including lines 1200 and 1224–1282) from null dereferences while preserving the ability to copy highlighted text.
Guard
shouldShowforcopyMessage:Accurately type
reportActionasOnyxEntry<ReportAction>inContextMenuActionPayload:And in
BaseReportActionContextMenu.tsx:407:Alternative Solutions Explored
reportAction &&solely to line 1200 (isTagModificationAction): Incomplete. It would still crash on line 1224 (UPDATE_FEATURE_ENABLED) and subsequent branches when none of the earlier branches match.if (selection): Rejected because copying user-highlighted text should remain functional even ifreportActionis nullish or detaching.magomedta commentedon Oct 22, 2026
Clarification / Proposal Refinement
To comprehensively protect not only line 1200 (
isTagModificationAction) but also all subsequent branches (lines 1224–1282, which also accessreportAction.actionName === ...), the most robust approach is:In
src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx:In
copyMessage'sonPress, immediately return early if!reportActionafter handling user text selection:This completely shields the entire remaining
else ifchain from null dereference while preserving the ability to copy highlighted text.In
shouldShowforcopyMessage, require!!reportAction:Accurately type
reportAction: OnyxEntry<ReportAction>inContextMenuActionPayloadand adjustBaseReportActionContextMenu.tsxaccordingly so TypeScript catches any future unguarded accesses at compile time.huult commentedon Oct 23, 2026
App/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
Line 1200 in ba97c03
App/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
Lines 1224 to 1282 in ba97c03
We added a new check using
reportAction.actionNamein theonPresshandler without anullcheck. This can cause aTypeError: Cannot read properties of null (reading 'actionName')whenreportActionisnulland one of these branches executes.App/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
Line 1200 in ba97c03
App/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
Lines 1224 to 1282 in ba97c03
We can fix this by adding an optional chaining check:
I suggest using
reportAction?.actionNamehere, consistent with the existingnullchecks. There's no need to hide the Copy action whenreportActionisnull. We just need to handle the null case safely.We can go with @MelvinBot's proposal. @MelvinBot, could you please review my comment? Thanks!
🎀 👀 🎀 c+ reviewed.
melvin-bot commentedon Oct 23, 2026
Current assignee @mountiny is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.
MelvinBot commentedon Oct 23, 2026
Agreed,
huult. Adding?.alone fixes the crash, and I'm fine dropping the other parts of my proposal (hiding Copy, changing the payload type, the Reply in thread guard, and the unit test).I checked the whole Copy
onPresschain atba97c03. These are the only places that readreportAction.actionNamewithout?.:isTagModificationAction)=== POLICY_CHANGE_LOG...checks)Why
?.is enoughreportAction?.actionNameor calls a helper such asisActionOfTypethat returns false for a missing action.reportAction.actorAccountIDat line 1161 and line 1305, run only after a type check passes, so they can't seenull.undefinedtoisTagModificationAction. That's safe because the function only does===comparisons (ReportActionsUtils.ts:2092).contentandmessageTextfallbacks at lines 1589-1594. Both are empty, so Copy copies nothing and closes the menu without crashing. A user who selected text still gets it copied at line 1101.To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/104746#issuecomment-6355600549)to create a draft PR that adds?.on line 1200 and lines 1224-1282.view run