Skip to content

[compiler] Fix invariant error for optional chaining in try/catch#387

Open
everettbu wants to merge 3 commits into
mainfrom
pr35573
Open

[compiler] Fix invariant error for optional chaining in try/catch#387
everettbu wants to merge 3 commits into
mainfrom
pr35573

Conversation

@everettbu

@everettbu everettbu commented Jan 20, 2026

Copy link
Copy Markdown

Mirror of facebook/react#35573
Original author: josephsavona


Optional chaining and other value blocks within try/catch blocks were throwing an Invariant error ("Unexpected terminal in optional") instead of the expected Todo error. This caused hard failures instead of graceful bailouts.

The issue occurred because DropManualMemoization and ValidateExhaustiveDependencies ran before BuildReactiveFunction, and encountered maybe-throw terminals in their switch statements without a handler, falling through to the invariant case.

Fixed by adding explicit maybe-throw cases in both files that throw the proper Todo error with the message "Support value blocks (conditional, logical, optional chaining, etc) within a try/catch statement".

Also renamed the existing bug test to reflect it's now correctly handled:

  • error.bug-invariant-unexpected-terminal-in-optional → error.todo-optional-chaining-within-try-catch

Closes #35570


Stack created with Sapling. Best reviewed with ReviewStack.

  • #35607
  • #35298
  • #35596
  • -> #35573
  • #35595
  • #35539

@everettbu everettbu added CLA Signed React Core Team Opened by a member of the React Core Team labels Jan 20, 2026
@greptile-apps

greptile-apps Bot commented Jan 20, 2026

Copy link
Copy Markdown

Greptile Summary

Fixed invariant error when optional chaining appears within try/catch blocks by adding explicit maybe-throw terminal handlers in DropManualMemoization and ValidateExhaustiveDependencies that throw proper Todo errors instead of falling through to invariant cases.

  • Added maybe-throw case handlers in two compiler passes that run before BuildReactiveFunction
  • Renamed test from error.bug-invariant-unexpected-terminal-in-optional to error.todo-optional-chaining-within-try-catch to reflect the corrected behavior
  • Added new test case for optional chaining within try/catch in async callbacks
  • Included comprehensive documentation for all 55 compiler passes to help with future development
  • Enhanced snap test runner with fixture status tracking for better debugging workflow

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix is straightforward and surgical - adds explicit error handling for a specific terminal type in two switch statements. The changes are well-tested with both existing and new test cases confirming proper error behavior. The documentation additions are supplementary improvements that don't affect runtime behavior.
  • No files require special attention

Important Files Changed

Filename Overview
compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts Added maybe-throw case to handle optional chaining in try/catch, throwing proper Todo error instead of invariant
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts Added maybe-throw case to handle optional chaining in try/catch, throwing proper Todo error instead of invariant
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/error.todo-optional-chaining-within-try-catch.js Renamed test from error.bug-invariant-unexpected-terminal-in-optional to reflect correct Todo error behavior
compiler/packages/babel-plugin-react-compiler/src/tests/fixtures/compiler/error.todo-optional-chaining-within-try-catch-in-callback.js Added new test case for optional chaining within try/catch in async callback
compiler/packages/snap/src/runner.ts Added fixture status tracking for autocomplete suggestions in watch mode

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments (1)

  1. compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-invariant-unexpected-terminal-in-optional.expect.md, line 22 (link)

    logic: test expectation file should be updated to reflect the new Todo error instead of the Invariant error

    the PR description states the test was renamed from error.bug-invariant-unexpected-terminal-in-optional to error.todo-optional-chaining-within-try-catch and the expectation should now show a Todo error with message "Support value blocks (conditional, logical, optional chaining, etc) within a try/catch statement" instead of the invariant error shown here

120 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

124 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +586 to +593
case 'maybe-throw': {
CompilerError.throwTodo({
reason: `Support value blocks (conditional, logical, optional chaining, etc) within a try/catch statement`,
description: null,
loc: terminal.loc,
suggestions: null,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: missing break statement after throwTodo() - while this doesn't cause issues since throwTodo never returns, adding a break would follow switch case best practices and improve code clarity

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts
Line: 586:593

Comment:
**style:** missing `break` statement after `throwTodo()` - while this doesn't cause issues since `throwTodo` never returns, adding a `break` would follow switch case best practices and improve code clarity

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +1019 to +1026
case 'maybe-throw': {
CompilerError.throwTodo({
reason: `Support value blocks (conditional, logical, optional chaining, etc) within a try/catch statement`,
description: null,
loc: terminal.loc,
suggestions: null,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: missing break statement after throwTodo() - while this doesn't cause issues since throwTodo never returns, adding a break would follow switch case best practices and improve code clarity

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts
Line: 1019:1026

Comment:
**style:** missing `break` statement after `throwTodo()` - while this doesn't cause issues since `throwTodo` never returns, adding a `break` would follow switch case best practices and improve code clarity

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Much nicer workflow for working through errors in the compiler:
* Run `yarn snap -w`, oops there are are errors
* Hit 'p' to select a fixture => the suggestions populate with recent failures, sorted alphabetically. No need to copy/paste the name of the fixture you want to focus on!
* tab/shift-tab to pick one, hit enter to select that one
* ...Focus on fixing that test...
* 'p' to re-enter the picker. Snap tracks the last state of each fixture and continues to show all tests that failed on their last run, so you can easily move on to the next one. The currently selected test is highlighted, making it easy to move to the next one.
* 'a' at any time to run all tests
* 'd' at any time to toggle debug output on/off (while focusing on a single test)
…opment

Autogenerated summaries of each of the compiler passes which allow agents to get the key ideas of a compiler pass, including key input/output invariants, without having to reprocess the file each time. In the subsequent diff this seemed to help.
Optional chaining and other value blocks within try/catch blocks were throwing an Invariant error ("Unexpected terminal in optional") instead of the expected Todo error. This caused hard failures instead of graceful bailouts.

The issue occurred because DropManualMemoization and ValidateExhaustiveDependencies ran before BuildReactiveFunction, and encountered `maybe-throw` terminals in their switch statements without a handler, falling through to the invariant case.

Fixed by adding explicit `maybe-throw` cases in both files that throw the proper Todo error with the message "Support value blocks (conditional, logical, optional chaining, etc) within a try/catch statement".

Also renamed the existing bug test to reflect it's now correctly handled:
- error.bug-invariant-unexpected-terminal-in-optional → error.todo-optional-chaining-within-try-catch

Closes #35570
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants