dotnet/wpf · error · InvalidOperationException

SR.RowCacheCannotModifyNonExistentLayout

Error message

SR.RowCacheCannotModifyNonExistentLayout

What it means

AddPageRange adds a range of pages to the completed row layout. If the layout was never completed (_isLayoutCompleted == false), there is no layout structure to modify, so an InvalidOperationException (SR.RowCacheCannotModifyNonExistentLayout) is thrown. It is an internal guard ensuring page-add updates only apply to a fully computed layout.

Solutions

  1. Wait for the layout to complete before applying page-added changes (only raise page-cache changes after _isLayoutCompleted is true)
  2. Ensure the initial RecalcRows/layout pass runs to completion before the paginator reports page changes
  3. If implementing custom change handling, check IsLayoutCompleted before calling page-mutation APIs

Example fix

// before
cache.AddPage(startPage, count);
// after
if (cache.IsLayoutCompleted)
{
    cache.AddPage(startPage, count);
}
else
{
    cache.RecalcRows(0, columns);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!cache.IsLayoutCompleted) { /* wait for layout or call RecalcRows */ }

Try / catch

try { cache.AddPage(startPage, count); } catch (InvalidOperationException) { pendingChanges.Enqueue((startPage, count)); }

Prevention

When it happens

Trigger: OnPageCacheChanged (or a RowCacheChange handler) routing an Add change through AddPageRange while the row layout has not finished being computed (_isLayoutCompleted is false).

Common situations: Pagination updates (page count changes) arriving while the initial row layout pass is still in progress; custom paginators issuing incremental page-added notifications before the first full layout completes; race between pagination events and layout completion.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/RowCache.cs:868

                //Add this page to the row
                newRow.AddPage(pageSize);
            }

            return newRow;
        }

        /// <summary>
        /// Given a range of pages, adds the pages to the existing row cache,
        /// adding new rows where necessary.
        /// </summary>
        /// <param name="startPage">The first page to add to the layout</param>
        /// <param name="count">The number of pages to add.</param>
        private RowCacheChange AddPageRange(int startPage, int count)
        {
            if (!_isLayoutCompleted)
            {
                throw new InvalidOperationException(SR.RowCacheCannotModifyNonExistentLayout);
            }

            int currentPage = startPage;
            int lastPage = startPage + count;

            int startRow = 0;
            int rowCount = 0;

            //First we check to see if startPage is such that we'd end up skipping
            //pages in the document -- that is, if the last page in our layout is currently
            //10 and start is 15, we need to fill in pages 11-14 as well.
            if (startPage > LastPageInCache + 1)
            {
                currentPage = LastPageInCache + 1;
            }

            //Get the last row in the layout
            RowInfo lastRow = _rowCache[_rowCache.Count - 1];

View on GitHub (pinned to 81131a70a4)