dotnet/wpf · error · InvalidOperationException

SR.OnlyFlowFixedSupported

Error message

SR.OnlyFlowFixedSupported

What it means

AnnotationHelper.CheckInputs throws InvalidOperationException when the enabled AnnotationService's Root viewer has a null Document. Annotation helpers only work against flow or fixed documents hosted in DocumentViewerBase, FlowDocumentReader, or FlowDocumentScrollViewer; if the viewer currently hosts no document there is nothing to annotate. The Invariant.Assert above guarantees the root is a supported viewer type, so a null Document is reported via this dedicated exception.

Solutions

  1. Ensure the viewer's Document property is set (and non-null) before calling AnnotationHelper methods.
  2. Delay annotation commands until document load completes (e.g. handle Loaded event of the viewer).
  3. Guard helper calls with a null check on the viewer's Document.
  4. Verify the annotation service is attached to the viewer that actually hosts the document, not a different instance.

Example fix

// before
AnnotationHelper.CreateTextStickyNoteForSelection(service, author, anchorRes, rects);

// after
if (fdvViewer.Document != null)
    AnnotationHelper.CreateTextStickyNoteForSelection(service, author, anchorRes, rects);
Defensive patterns

Strategy: validation

Validate before calling

var viewer = service.Root as DocumentViewerBase ?? (DependencyObject)service.Root as FlowDocumentScrollViewer;
bool documentLoaded = viewer != null &&
    (viewer is DocumentViewerBase dv ? dv.Document != null : ((FlowDocumentScrollViewer)viewer).Document != null);
if (!documentLoaded) return; // wait for document load

Type guard

bool HasDocument(AnnotationService s) =>
    (s.Root as DocumentViewerBase)?.Document != null ||
    (s.Root as FlowDocumentScrollViewer)?.Document != null;

Try / catch

try { AnnotationHelper.CreateTextStickyNoteForSelection(service, ...); }
catch (InvalidOperationException) { /* defer until document is loaded */ }

Prevention

When it happens

Trigger: Calling an AnnotationHelper method while the service is enabled but its Root viewer (e.g. DocumentViewerBase.Document or FlowDocumentScrollViewer.Document) is null — the viewer has no document loaded.

Common situations: Invoking annotation commands from UI wiring before the document is set on the viewer; document unloaded asynchronously while a helper is invoked; binding or navigation that clears Document during startup.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Annotations/AnnotationHelper.cs:1098

            ArgumentNullException.ThrowIfNull(service);

            if (!service.IsEnabled)
            {
                throw new ArgumentException(SR.AnnotationServiceNotEnabled, nameof(service));
            }

            DocumentViewerBase viewer = service.Root as DocumentViewerBase;
            if (viewer == null)
            {
                FlowDocumentScrollViewer scrollViewer = service.Root as FlowDocumentScrollViewer;
                FlowDocumentReader reader = service.Root as FlowDocumentReader;
                Invariant.Assert((scrollViewer != null) || (reader != null), "Service's Root must be either a FlowDocumentReader, DocumentViewerBase or a FlowDocumentScrollViewer.");
            }
            else
            {
                if (viewer.Document == null)
                {
                    throw new InvalidOperationException(SR.OnlyFlowFixedSupported);
                }
            }
        }

        /// <summary>
        ///     Determines if a command should be enabled based on four things:
        ///      1.  Existing of a service
        ///      2.  Service is enabled
        ///      3.  Selection available
        ///      4.  Selection is not empty (optional)
        /// </summary>
        /// <param name="sender">DocumentViewerBase the command will operate on</param>
        /// <param name="checkForEmpty">whether to check for empty selection or not</param>
        /// <returns>true if the command should be enabled; false otherwise</returns>
        private static bool IsCommandEnabled(object sender, bool checkForEmpty)
        {
            Invariant.Assert(sender != null, "Parameter 'sender' is null.");

View on GitHub (pinned to 81131a70a4)