dotnet/wpf · error · InvalidOperationException

SR.Format(SR.InvalidOperation_MustImplementIPCCSOrHandleNavi…

Error message

SR.Format(SR.InvalidOperation_MustImplementIPCCSOrHandleNavigating, _bp != null ? _bp.GetType().ToString() : "null")

What it means

After AddBackEntry() journals the request, the resulting entry must carry a CustomContentState — either because the content implements IProvideCustomContentState or because a Navigating event handler supplied state. If je.CustomContentState is null, the service rolls back (RemoveBackEntry) and throws InvalidOperationException MustImplementIPCCSOrHandleNavigating naming the content type (or "null").

Solutions

  1. Implement IProvideCustomContentState on the navigated content class and return a CustomContentState from GetContentState()
  2. Handle the Navigating event and set e.ContentStateToSave to a CustomContentState instance
  3. Pass an explicit non-null CustomContentState to AddBackEntry(state)

Example fix

// before
navService.AddBackEntry(null); // page does not provide state
// after
public class MyPage : Page, IProvideCustomContentState
{
    public CustomContentState GetContentState() => new MyPageState(this);
}
navService.AddBackEntry(null); // now valid via IProvideCustomContentState
Defensive patterns

Strategy: validation

Validate before calling

bool providesState = navService.Content is IProvideCustomContentState || navigatingHandlerInstalled;
if (!providesState) navService.AddBackEntry(new MyState()); // explicit state

Type guard

bool ContentProvidesState(NavigationService ns) => ns.Content is IProvideCustomContentState;

Try / catch

try { ns.AddBackEntry(state); }
catch (InvalidOperationException ex) when (ex.Message.Contains("CustomContentState")) {
    // content type must implement IProvideCustomContentState
}

Prevention

When it happens

Trigger: Calling AddBackEntry(null) (or relying on it) when the current content object does not implement IProvideCustomContentState and no Navigating handler sets e.ContentStateToSave, so the journal entry ends with CustomContentState == null.

Common situations: Copy/pasting AddBackEntry sample code without implementing IProvideCustomContentState on the page class; renaming refactoring that dropped the interface; expecting AddBackEntry(null) to snapshot the page automatically (it does not).

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

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

            }
            if (_bp == null)
                throw new InvalidOperationException(SR.InvalidOperation_AddBackEntryNoContent);

            _customContentStateToSave = state;
            JournalEntry je = UpdateJournal(NavigationMode.New, JournalReason.AddBackEntry, null);
            // Controls state is not saved by design (saveContent=false). If client applications
            // require it to be synchronized with the CustomContentState, they can explicitly
            // include it.

            _customContentStateToSave = null;

            // Since state=null is allowed on input, make sure we get an object either via the
            // IProvideCustomContentState interface or from a Navigating event handler.
            // Otherwise it doesn't make sense to add a journal entry.
            if (je != null && je.CustomContentState == null)
            {
                RemoveBackEntry();
                throw new InvalidOperationException(
                    SR.Format(SR.InvalidOperation_MustImplementIPCCSOrHandleNavigating,
                            _bp != null ? _bp.GetType().ToString() : "null"));
            }
        }

        /// <summary>
        /// Remove the first JournalEntry from NavigationWindow's back history
        /// </summary>
        public JournalEntry RemoveBackEntry()
        {
            if (IsDisposed)
            {
                return null;
            }
            if (JournalScope == null)
                return null; //(Normally, no exception is thrown if there is no back entry.)
            return JournalScope.RemoveBackEntry();
        }

View on GitHub (pinned to 81131a70a4)