dotnet/wpf · error · InvalidOperationException
SR.FlowDocumentFormattingReentrancy
Error message
SR.FlowDocumentFormattingReentrancy
What it means
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.
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.
Example fix
// before
void OnPaginationProgress(object sender, PaginationProgressEventArgs e) {
paginator.GetPageAsync(e.Start + e.Count, null); // inline during formatting
}
// after
void OnPaginationProgress(object sender, PaginationProgressEventArgs e) {
Dispatcher.BeginInvoke(new Action(() =>
paginator.GetPageAsync(e.Start + e.Count, null)),
System.Windows.Threading.DispatcherPriority.Background);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!paginator.Document.StructuralCache.IsFormattingInProgress &&
!paginator.Document.StructuralCache.IsContentChangeInProgress)
paginator.GetPageAsync(pageNumber, userState); Type guard
bool CanPaginate(FlowDocument doc) => !doc.StructuralCache.IsFormattingInProgress && !doc.StructuralCache.IsContentChangeInProgress;
Try / catch
try { paginator.GetPageAsync(n, state); }
catch (InvalidOperationException) {
Dispatcher.BeginInvoke(new Action(() => paginator.GetPageAsync(n, state)), DispatcherPriority.Background);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- SR.FlowDocumentFormattingReentrancy
- SR.FlowDocumentInvalidContnetChange
- SR.TextContainerChangingReentrancyInvalid
- SR.TextContainerChangingReentrancyInvalid
- args
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/623e8cd4bf87009c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/FlowDocumentPaginator.cs:79
/// Async version of <see cref="DocumentPaginator.GetPage"/>
/// </summary>
/// <param name="pageNumber">Page number.</param>
/// <param name="userState">Unique identifier for the asynchronous task.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Throws ArgumentOutOfRangeException if PageNumber is negative.
/// </exception>
public override void GetPageAsync(int pageNumber, object userState)
{
// Page number cannot be negative.
if (pageNumber < 0)
{
throw new ArgumentOutOfRangeException(nameof(pageNumber), SR.IDPNegativePageNumber);
}
// Reentrancy check.
if (_document.StructuralCache.IsFormattingInProgress)
{
throw new InvalidOperationException(SR.FlowDocumentFormattingReentrancy);
}
if (_document.StructuralCache.IsContentChangeInProgress)
{
throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid);
}
DocumentPage page = null;
if (!_backgroundPagination)
{
page = GetPage(pageNumber);
}
else
{
// If entire content has been already pre-paginated (BreakRecordTable is clean)
// and requesting non-existing page number, return DocumentPage.Missing.
if (_brt.IsClean && !_brt.HasPageBreakRecord(pageNumber))
{View on GitHub (pinned to 81131a70a4)