{"record":{"id":"87ca8118e4ddff44","repo":"angular/components","slug":"error-attempted-to-measure-an-item-that-isn-t-ren","errorCode":null,"errorMessage":"Error: attempted to measure an item that isn't rendered.","messagePattern":"Error: attempted to measure an item that isn't rendered\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cdk/scrolling/virtual-for-of.ts","lineNumber":215,"sourceCode":"      this._onRenderedDataChange();\n    });\n    this._viewport.attach(this);\n  }\n\n  /**\n   * Measures the combined size (width for horizontal orientation, height for vertical) of all items\n   * in the specified range. Throws an error if the range includes items that are not currently\n   * rendered.\n   */\n  measureRangeSize(range: ListRange, orientation: 'horizontal' | 'vertical'): number {\n    if (range.start >= range.end) {\n      return 0;\n    }\n    if (\n      (range.start < this._renderedRange.start || range.end > this._renderedRange.end) &&\n      (typeof ngDevMode === 'undefined' || ngDevMode)\n    ) {\n      throw Error(`Error: attempted to measure an item that isn't rendered.`);\n    }\n\n    // The index into the list of rendered views for the first item in the range.\n    const renderedStartIndex = range.start - this._renderedRange.start;\n    // The length of the range we're measuring.\n    const rangeLen = range.end - range.start;\n\n    // Loop over all the views, find the first and land node and compute the size by subtracting\n    // the top of the first node from the bottom of the last one.\n    let firstNode: HTMLElement | undefined;\n    let lastNode: HTMLElement | undefined;\n\n    // Find the first node by starting from the beginning and going forwards.\n    for (let i = 0; i < rangeLen; i++) {\n      const view = this._viewContainerRef.get(i + renderedStartIndex) as EmbeddedViewRef<\n        CdkVirtualForOfContext<T>\n      > | null;\n      if (view && view.rootNodes.length) {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/scrolling/virtual-for-of.ts#L197-L233","documentation":"CdkVirtualForOf.measureRangeSize(range, options) can only measure items that are currently rendered inside the viewport. The method throws when the requested index range falls outside the viewport's currently rendered range, since unrendered views have no DOM to measure.","triggerScenarios":"Calling measureRangeSize with a range whose start is before the rendered range start or whose end exceeds the rendered range end; measuring an index while scrolled far from it; measuring immediately after data changes before the rendered range updates.","commonSituations":"Programmatic autoscroll/measurement features that assume all items are rendered; measuring during fast scrolling when views outside the range were recycled; calling measureRangeSize before the viewport has rendered the requested indexes (e.g. right after pushing data).","solutions":["Only call measureRangeSize for indexes within the currently rendered range (viewport.getRenderedRange())","Clamp the requested range to the rendered range before measuring","Use the uniform measureRangeSize option 'header/footer' semantics carefully: when measuring all items, ensure the whole list is rendered (e.g. temporarily render all or measure only visible items)","Defer measurement until after rendering completes (afterNextRender / requestAnimationFrame) and re-check the rendered range"],"exampleFix":"// before\nconst range = { start: 0, end: this.data.length };\nthis.viewport.measureRangeSize(range);\n// after\nconst rendered = this.viewport.getRenderedRange();\nconst range = {\n  start: Math.max(0, rendered.start),\n  end: Math.min(this.data.length, rendered.end),\n};\nif (range.end > range.start) {\n  this.viewport.measureRangeSize(range);\n}","handlingStrategy":"validation","validationCode":"const rendered = viewport.getRenderedRange();\nconst clamped = {\n  start: Math.max(range.start, rendered.start),\n  end: Math.min(range.end, rendered.end),\n};\nif (clamped.end > clamped.start) {\n  viewport.measureRangeSize(clamped, options);\n}","typeGuard":"function isWithinRenderedRange(range: ListRange, viewport: CdkVirtualScrollViewport): boolean {\n  const rendered = viewport.getRenderedRange();\n  return range.start >= rendered.start && range.end <= rendered.end;\n}","tryCatchPattern":"try {\n  viewport.measureRangeSize(range, options);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"isn't rendered\")) {\n    console.warn('Range not rendered; skipping measurement for', range);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Check viewport.getRenderedRange() before measuring","Defer measurement until after rendering (requestAnimationFrame / afterNextRender)","Never assume all list items are rendered in a virtual viewport","Re-measure only when data and scroll position are stable"],"tags":["angular","cdk","virtual-scroll","measure","dom"],"backgroundTag":"measure-unrendered-item","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}