{"record":{"id":"d84c3f80ad3a80bd","repo":"dotnet/wpf","slug":"sr-rowcachepagenotfound","errorCode":null,"errorMessage":"SR.RowCachePageNotFound","messagePattern":"SR\\.RowCachePageNotFound","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/RowCache.cs","lineNumber":288,"sourceCode":"\n            //Search our cache for the row that contains the page.\n            //NOTE: Future perf item:\n            //This search can be re-written as a binary search, which will be O(log(N))\n            //instead of O(N).\n            for (int i = 0; i < _rowCache.Count; i++)\n            {\n                RowInfo rowInfo = _rowCache[i];\n                if (pageNumber >= rowInfo.FirstPage &&\n                    pageNumber < rowInfo.FirstPage + rowInfo.PageCount)\n                {\n                    //We found the row, return the index.\n                    return i;\n                }\n            }\n\n            //We didn't find it.  Something is very likely wrong with our layout.\n            //We'll throw, as this is an indicator that our layout cannot be trusted.\n            throw new InvalidOperationException(SR.RowCachePageNotFound);\n        }\n\n        /// <summary>\n        /// Returns the row that lives at the specified vertical offset.\n        /// </summary>\n        /// <param name=\"offset\">The vertical offset to find the corresponding row for</param>\n        /// <returns>The index of the row that lives at the offset.</returns>\n        public int GetRowIndexForVerticalOffset(double offset)\n        {\n            ArgumentOutOfRangeException.ThrowIfNegative(offset);\n            ArgumentOutOfRangeException.ThrowIfGreaterThan(offset, ExtentHeight);\n\n            //If we have no rows we'll return 0 (the top of the non-existent document)\n            if (_rowCache.Count == 0)\n            {\n                return 0;\n            }\n","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/RowCache.cs#L270-L306","documentation":"RowCache.GetRowIndexForPageNumber scans its cached rows for the row containing the requested page number. If no row covers that page, the internal layout model is inconsistent (a page exists in the PageCache but no row claims it), so InvalidOperationException(SR.RowCachePageNotFound) is thrown rather than returning wrong layout data.","triggerScenarios":"Requesting GetRowForPageNumber (or triggering RecalcRowsForFixedPageSizes) with a page number outside the ranges covered by cached rows — typically after layout/dynamic content changes desynchronized the RowCache from the PageCache.","commonSituations":"Dynamic content resizing in DocumentViewer/FlowDocumentPageViewer while pages are being fetched; reporting/printing pipelines asking for pages before row recalculation finishes; bugs in custom IDynamicDocumentPaginator page counts.","solutions":["Invalidate and rebuild caches together: reset the RowCache whenever the PageCache or document content changes (call the viewer's InvalidateScrollInfo/Recalc paths).","Verify the paginator's PageCount is consistent with the rows before requesting a page.","Ensure content changes during paging raise the proper change notifications so the cache recalculates.","Capture the page number and stack for a framework bug report if reproducible with stock FlowDocument content."],"exampleFix":"// before\nvar row = rowCache.GetRowForPageNumber(pageNumber); // stale cache -> throw\n// after\nrowCache.RecalcRowsForFixedPageSizes(); // or otherwise invalidate on content change\nvar row = rowCache.GetRowForPageNumber(Math.Min(pageNumber, paginator.PageCount - 1));","handlingStrategy":"try-catch","validationCode":"bool pageInCache = pageNumber >= 0 && pageNumber < paginator.PageCount && rowCache.RowCount > 0;","typeGuard":"bool RowCacheIsConsistent(RowCache rc, int page) =>\n    rc != null && rc.PageCache != null && page >= 0;","tryCatchPattern":"try\n{\n    row = rowCache.GetRowForPageNumber(pageNumber);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"page\"))\n{\n    // cache/layout desync: invalidate caches and retry once\n    rowCache.RecalcRowsForFixedPageSizes();\n    row = rowCache.GetRowForPageNumber(pageNumber);\n}","preventionTips":["Invalidate RowCache whenever document content or PageCache changes","Clamp requested page numbers to the paginator's PageCount","Let change notifications drive recalculation instead of manual caching"],"tags":["wpf","row-cache","layout","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}