dotnet/wpf · error · InvalidOperationException

SR.InvalidOperation_CantChangeJournalOwnership

Error message

SR.InvalidOperation_CantChangeJournalOwnership

What it means

NavigationService.InvalidateJournalNavigationScope() detaches the service from its journal, but refuses while a Back/Forward (uncommitted) journal navigation is pending, throwing InvalidOperationException CantChangeJournalOwnership. Changing journal ownership mid-navigation would lose the pending navigation target.

Solutions

  1. Defer RemoveChild/reparenting until navigation completes (LoadCompleted/NavigationFailed)
  2. Do not call GoBack/GoForward immediately before removing a frame; commit pending navigations first
  3. Restructure so journal ownership changes happen outside navigation event handlers

Example fix

// before
navService.GoBack();
parent.RemoveChild(frame); // pending back navigation -> throws
// after
navService.GoBack();
navService.LoadCompleted += (s, e) => parent.RemoveChild(frame);
Defensive patterns

Strategy: validation

Validate before calling

bool pending = frame.NavigationService.GetType()
    .GetProperty("Journal", BindingFlags.NonPublic|BindingFlags.Instance) != null; // in practice: defer removal
if (isNavigating) DeferRemoval(frame); else parent.RemoveChild(frame);

Try / catch

try { parent.RemoveChild(frame); }
catch (InvalidOperationException ex) when (ex.Message.Contains("journal")) {
    frame.NavigationService.LoadCompleted += (s, e) => parent.RemoveChild(frame);
}

Prevention

When it happens

Trigger: Calling RemoveChild (e.g. detaching a child frame/navigation service from a parent) while _journalScope.Journal.HasUncommittedNavigation is true — a Back or Forward navigation was started but not yet committed.

Common situations: Removing/reparenting a Frame from the visual tree in response to navigation events; tearing down navigation hosts in Navigating/FragmentNavigation handlers; dynamic layout that swaps frames during user back/forward clicks.

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/dfdc3efb80d75571. Report an issue: GitHub.

Appendix: source

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

        /// <returns></returns>
        private static INavigatorBase FindTargetInNavigationWindow(NavigationWindow navigationWindow, string navigatorId)
        {
            if (navigationWindow != null)
            {
                return navigationWindow.NavigationService.FindTarget(navigatorId);
            }
            return null;
        }

        internal void InvalidateJournalNavigationScope()
        {
            // If there is a pending journal navigation (Back/Fwd), the JournalNavigationScope cannot
            // be changed. (If it is a _new_ navigation, we're OK; it will be recorded in the new
            // applicable journal.)
            // _navStatus or _navigateQueueItem are not checked here, because they are set only after
            // raising the Navigating event, while an event handler might cause journal ownership to change.
            if (_journalScope != null && _journalScope.Journal.HasUncommittedNavigation)
                throw new InvalidOperationException(SR.InvalidOperation_CantChangeJournalOwnership);

            _journalScope = null;

            for (int i = ChildNavigationServices.Count - 1; i >= 0; i--)
            {
                ((NavigationService)ChildNavigationServices[i]).InvalidateJournalNavigationScope();
            }
        }

        internal void OnParentNavigationServiceChanged()
        {
            NavigationService oldParent = _parentNavigationService;
            NavigationService newParent = ((DependencyObject)INavigatorHost).GetValue(NavigationServiceProperty) as NavigationService;

            if (newParent == oldParent)
                return;

            // Remove from old parent's list

View on GitHub (pinned to 81131a70a4)