{"record":{"id":"623e8cd4bf87009c","repo":"dotnet/wpf","slug":"sr-flowdocumentformattingreentrancy-flowdocumentpaginator","errorCode":null,"errorMessage":"SR.FlowDocumentFormattingReentrancy","messagePattern":"SR\\.FlowDocumentFormattingReentrancy","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/FlowDocumentPaginator.cs","lineNumber":79,"sourceCode":"        /// Async version of <see cref=\"DocumentPaginator.GetPage\"/>\n        /// </summary>\n        /// <param name=\"pageNumber\">Page number.</param>\n        /// <param name=\"userState\">Unique identifier for the asynchronous task.</param>\n        /// <exception cref=\"ArgumentOutOfRangeException\">\n        /// Throws ArgumentOutOfRangeException if PageNumber is negative.\n        /// </exception>\n        public override void GetPageAsync(int pageNumber, object userState)\n        {\n            // Page number cannot be negative.\n            if (pageNumber < 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(pageNumber), SR.IDPNegativePageNumber);\n            }\n\n            // Reentrancy check.\n            if (_document.StructuralCache.IsFormattingInProgress)\n            {\n                throw new InvalidOperationException(SR.FlowDocumentFormattingReentrancy);\n            }\n            if (_document.StructuralCache.IsContentChangeInProgress)\n            {\n                throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid);\n            }\n\n            DocumentPage page = null;\n\n            if (!_backgroundPagination)\n            {\n                page = GetPage(pageNumber);\n            }\n            else\n            {\n                // If entire content has been already pre-paginated (BreakRecordTable is clean)\n                // and requesting non-existing page number, return DocumentPage.Missing.\n                if (_brt.IsClean && !_brt.HasPageBreakRecord(pageNumber))\n                {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/FlowDocumentPaginator.cs#L61-L97","documentation":"FlowDocumentPaginator.GetPageAsync throws InvalidOperationException(SR.FlowDocumentFormattingReentrancy) when GetPageAsync is called while formatting is already in progress on the FlowDocument (StructuralCache.IsFormattingInProgress). Pagination drives formatting internally; a nested request would corrupt the incremental formatting state, so WPF throws.","triggerScenarios":"Calling GetPageAsync from within a callback that executes during formatting — e.g. inside GetPage's synchronous work, inside PaginationProgress/PaginationCompleted handlers when they run inline, or from a thread re-entering the paginator while a prior GetPageAsync is executing its formatting phase.","commonSituations":"Chained pagination event handlers that immediately request more pages; custom paginators that call base GetPageAsync inside OnGetPage; UI code that forces pagination synchronously from a layout event.","solutions":["Do not call GetPageAsync from pagination/formatting callbacks; queue the request and issue it after the current operation completes (e.g. via Dispatcher.BeginInvoke).","Use the paginator's async completion events to trigger subsequent page requests only after handlers return.","Avoid calling GetPage (synchronous) from code paths that can run while an async GetPageAsync is mid-flight on the same dispatcher.","Share one paginator per FlowDocument and serialize access to GetPageAsync."],"exampleFix":"// before\nvoid OnPaginationProgress(object sender, PaginationProgressEventArgs e) {\n    paginator.GetPageAsync(e.Start + e.Count, null); // inline during formatting\n}\n// after\nvoid OnPaginationProgress(object sender, PaginationProgressEventArgs e) {\n    Dispatcher.BeginInvoke(new Action(() =>\n        paginator.GetPageAsync(e.Start + e.Count, null)),\n        System.Windows.Threading.DispatcherPriority.Background);\n}","handlingStrategy":"try-catch","validationCode":"if (!paginator.Document.StructuralCache.IsFormattingInProgress &&\n    !paginator.Document.StructuralCache.IsContentChangeInProgress)\n    paginator.GetPageAsync(pageNumber, userState);","typeGuard":"bool CanPaginate(FlowDocument doc) => !doc.StructuralCache.IsFormattingInProgress && !doc.StructuralCache.IsContentChangeInProgress;","tryCatchPattern":"try { paginator.GetPageAsync(n, state); }\ncatch (InvalidOperationException) {\n    Dispatcher.BeginInvoke(new Action(() => paginator.GetPageAsync(n, state)), DispatcherPriority.Background);\n}","preventionTips":["Never request pages from PaginationProgress/PaginationCompleted handlers inline","Defer with Dispatcher.BeginInvoke in event-driven flows","Serialize pagination access; no nested GetPage/GetPageAsync calls","Use one paginator per FlowDocument"],"tags":["wpf","reentrancy","flowdocument","pagination"],"backgroundTag":"unsupported-operation","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}