dotnet/wpf · error · InvalidOperationException

SR.InvalidOperation_CannotReenterPageFunction

Error message

SR.InvalidOperation_CannotReenterPageFunction

What it means

Thrown by NavigationService when a navigation is attempted while a PageFunction is still in an active Return cycle. The library checks whether the root element is a PageFunctionBase that is resuming (_Resume) or still holds a ReturnEventSaver (_Saver), which indicates the PageFunction's Return event has not fully completed and re-entering navigation would corrupt journal state.

Solutions

  1. Finish the PageFunction lifecycle first: only call OnReturn once and navigate away after the Return event handler completes.
  2. Move re-navigation logic out of the Return event handler (e.g. queue it via Dispatcher.BeginInvoke).
  3. Check that no Return event handler is attached when you intend a plain navigation to the PageFunction.
  4. Restructure to use a new PageFunction instance instead of re-entering the one mid-Return.

Example fix

// before: inside Return event handler
void pf_Return(object sender, ReturnEventArgs<string> e) {
    navService.Navigate(new NextPage()); // can re-enter mid-Return
}
// after
void pf_Return(object sender, ReturnEventArgs<string> e) {
    Dispatcher.BeginInvoke(() => navService.Navigate(new NextPage()));
}
Defensive patterns

Strategy: try-catch

Validate before calling

var pf = root as PageFunctionBase;
bool unsafeToNavigate = pf != null && (pf._Resume || pf._Saver != null);
if (unsafeToNavigate) { /* defer navigation */ }

Type guard

bool IsInReturnCycle(object root) => root is PageFunctionBase pf && (pf._Resume || pf._Saver != null);

Try / catch

try { navService.Navigate(target); }
catch (InvalidOperationException ex) when (ex.Message.Contains("reenter") || ex.Message.Contains("PageFunction"))
{ Dispatcher.BeginInvoke(() => navService.Navigate(target)); }

Prevention

When it happens

Trigger: Calling Navigate/NavigateToParentPage with a root visual that is a PageFunctionBase whose _Resume flag is set or whose _Saver is non-null — i.e. navigating to a PageFunction again before its OnReturn/Return event has been fully processed.

Common situations: Re-navigating to a PageFunction from inside its own Return event handler; calling Navigate on a PageFunction still finishing a child PageFunction's Return; nested PageFunction flows where the parent is navigated to while the child's result is in flight.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/e95a0d6bb4b605c8. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Navigation/NavigationService.cs:1632

            if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordHosting | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
            {
                EventTrace.EventProvider.TraceEvent(
                    EventTrace.Event.Wpf_NavigationStart, EventTrace.Keyword.KeywordHosting | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info,
                    navigateInfo != null ? navigateInfo.NavigationMode.ToString() : NavigationMode.New.ToString(),
                    root != null ? root.ToString() : "(null)");
            }

            Invariant.Assert(IsConsistent(navigateInfo));

            // Prevent re-starting the same PageFunction object before it has returned first.
            if (navigateInfo == null) // not called internally, from NavigateToParentPage()
            {
                PageFunctionBase pf = root as PageFunctionBase;
                // This won't detect the case when no Return event handler was attached, but then
                // we don't run the risk of overwriting the ReturnEventSaver.
                if (pf != null && (pf._Resume || pf._Saver != null))
                    throw new InvalidOperationException(SR.InvalidOperation_CannotReenterPageFunction);
            }

            Uri source = navigateInfo?.Source;

            // HandleNavigating will set the pending Uri from navigationState if available
            // See comments in NavigateInfo class
            if (!HandleNavigating(source, root, navigationState, null, false))
            {
                return false;
            }

            // root==_bp occurs in these cases:
            //   - Navigate(object) was called with the current Content object. This is handled as fragment
            //      navigation, scrolling content to top.
            //   - Refresh(). We'll go through the entire navigation sequence.
            //   - Going back/fwd to a journal entry associated with the same object. This is also handled
            //      as fragment navigation.
            if (object.ReferenceEquals(root, _bp) && (navigateInfo == null || navigateInfo.NavigationMode != NavigationMode.Refresh))

View on GitHub (pinned to 81131a70a4)