Skip to content

[go_router] Fix onException recovery with onEnter - #12216

Open
davidmigloz wants to merge 3 commits into
flutter:mainfrom
davidmigloz:upstream-onexception-initial-nav-fix
Open

davidmigloz wants to merge 3 commits into
flutter:mainfrom
davidmigloz:upstream-onexception-initial-nav-fix

Conversation

@davidmigloz

@davidmigloz davidmigloz commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

When onException starts a recovery navigation while a parse involving onEnter is still completing, the recovery can be overwritten by the outer navigation.

With onEnter, parser completion crosses an async boundary. Calling onException before returning the parser result can start a re-entrant navigation before Flutter's Router captures the outer transaction. The outer parse can then reuse the transaction token and apply its stale result after the recovery navigation.

There is a second edge case in the same handler: returning routerDelegate.currentConfiguration unconditionally is invalid during initial navigation, when no configuration has committed yet. It can leave the app blank or trip the delegate's configuration assertion. Conversely, once a route or error configuration has committed, a later parsing failure should not replace it when onException does not navigate.

This change handles both cases centrally in parserExceptionHandler:

  • When a top-level onEnter is configured, schedule onException in a microtask so Flutter's Router captures the outer navigation before recovery begins. Without onEnter, onException remains synchronous.
  • Return the current configuration when it is a committed route or error configuration. If no valid configuration exists yet, return the parser's error result so the delegate always receives a valid configuration.

The parser continues to invoke this handler synchronously and uses its returned RouteMatchList; there is no path-specific exception handling in parser.dart.

Regression coverage includes:

  • synchronous and app-deferred recovery from a blocked initial navigation;
  • synchronous recovery from unmatched initial and subsequent locations with onEnter configured;
  • a non-navigating onException on unmatched initial navigation;
  • preserving an installed error configuration across a second unmatched navigation;
  • preserving an installed error configuration when a later navigation is blocked by onEnter.

Validated after rebasing onto current main (go_router 18.1.2):

  • focused exception_handling_test.dart suite;
  • package and example test suites on the VM, Chrome, and Chrome/Wasm;
  • repository format, analyze, validate, and publish dry-run checks.

Fixes flutter/flutter#189582

Pre-Review Checklist

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2

@github-actions github-actions Bot added p: go_router triage-framework Should be looked at in framework triage labels Jul 16, 2026
davidmigloz added a commit to davidmigloz/flutter_packages that referenced this pull request Aug 3, 2026
…avigation

When onEnter blocks the very first navigation there is no prior route to
restore, and onException handling returned an empty configuration that
tripped the setNewRoutePath assertion. Falls back to the error match list
when the current configuration is empty, defers onParserException via a
microtask in the no-prior-route branch only, and preserves valid error
configurations in parserExceptionHandler.

Squash of the two commits on the upstream PR branch:
flutter#12216
@tarrinneal

Copy link
Copy Markdown
Contributor

From traige: Is this pr still being worked on?

@tarrinneal tarrinneal added the waiting for response The Flutter team cannot make further progress on this PR until the author responds label Oct 6, 2026
@davidmigloz
davidmigloz force-pushed the upstream-onexception-initial-nav-fix branch from 00aeed1 to 3303cf8 Compare October 6, 2026 21:40
@davidmigloz

davidmigloz commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Yes, thanks for the ping. I have rebased this onto current main (go_router 18.0.2), resolved the intervening test-import migration, rerun the package and example suites on the VM, Chrome, and Chrome/Wasm, and refreshed the description. I am marking it ready for review now.

@davidmigloz
davidmigloz marked this pull request as ready for review October 6, 2026 21:41
@github-actions github-actions Bot removed the waiting for response The Flutter team cannot make further progress on this PR until the author responds label Oct 6, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request defers the execution of onParserException using a microtask in GoRouteInformationParser to prevent recovery navigations from being discarded during an initial parse. Additionally, it updates parserExceptionHandler in GoRouter to fall back to the error match list when the current configuration is empty and not an error, preventing an assertion failure in GoRouterDelegate.setNewRoutePath. Relevant unit tests and a changelog entry have been added. There are no review comments, so no further feedback is provided.

@Piinks
Piinks self-requested a review October 20, 2026 20:52
@loic-sharma

Copy link
Copy Markdown
Member

@Piinks ping from triage :)

@Piinks Piinks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for tracking this down and adding test coverage for the blocked initial navigation flow.

While tracing through how Flutter's Router handles transaction tokens during parseRouteInformationWithDependencies, I noticed that the same token-stealing race still happens on other parser error paths whenever onEnter is configured, and there is a small inconsistency between how onCanNotEnter in parser.dart and parserExceptionHandler in router.dart handle already-installed error configurations. I left inline comments with details and reproduction cases.

Comment thread packages/go_router/lib/src/parser.dart Resolved
Comment thread packages/go_router/lib/src/router.dart Resolved
…avigation

When onException fires during the initial navigation (e.g., an initial
deep link blocked by onEnter returning Block.stop()),
routerDelegate.currentConfiguration is empty. Returning it directly from
parserExceptionHandler causes GoRouterDelegate.setNewRoutePath to hit
`assert(configuration.isNotEmpty || configuration.isError)`.

Fall back to the error match list (isError=true) when the current
configuration is empty, satisfying the assertion while any recovery
navigation queued from onException is processed.

Additionally, defer the onParserException call itself to a microtask on
that same initial-navigation, blocked-with-no-prior-route path. Calling
it synchronously let the app crash-prevention fix land, but a
router.go() made synchronously from onException was silently discarded:
Flutter's Router mints a new intent token for the re-entrant parse while
the initial parse is still in-flight, dropping the outer result. The
deferral is scoped to that one parser.dart call site so ordinary initial
navigation exceptions (e.g. a plain unmatched route) keep calling
onException synchronously, as existing tests rely on.
Move the deferral to the central parser exception handler so every parser error path reached through onEnter gets the same transaction ordering. Restore the parser handler return value so an installed error configuration is preserved when a later navigation is blocked. Add initial, subsequent, and installed-error regression coverage.
@davidmigloz
davidmigloz force-pushed the upstream-onexception-initial-nav-fix branch from 3303cf8 to 1124f35 Compare October 30, 2026 22:53
@davidmigloz davidmigloz changed the title [go_router] Fix assertion failure when onException fires on initial navigation [go_router] Fix onException recovery with onEnter Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p: go_router triage-framework Should be looked at in framework triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[go_router] Assertion failure in setNewRoutePath when onException fires on the initial navigation

4 participants