dotnet/wpf · error · InvalidOperationException
SR.Format(SR.CannotRemoveUnrealizedItems, index, count)
Error message
SR.Format(SR.CannotRemoveUnrealizedItems, index, count)
What it means
Remove/Recycle walks the item map blocks from the given position and requires every block in the removal range to be a RealizedItemBlock. If it encounters an unrealized block within the range, the generator throws InvalidOperationException (CannotRemoveUnrealizedItems) because unrealized items have no containers to remove or recycle.
Solutions
- Ensure the removal range contains only realized items: check ContainerFromIndex/ShouldRealize before including an item.
- Recalculate the position and count against the current item map after any CollectionChanged event.
- Use Recycle semantics only on containers you actually obtained from GenerateNext in this pass.
Example fix
// before
generator.Remove(startPos, pageItemCount, true); // range may include unrealized items
// after
int realized = 0;
while (realized < pageItemCount && generator.ContainerFromIndex(firstIndex + realized) != null)
realized++;
if (realized > 0)
generator.Remove(startPos, realized, true); Defensive patterns
Strategy: validation
Validate before calling
int realizedCount = 0;
for (int i = firstIndex; i <= lastIndex; i++)
if (generator.ContainerFromIndex(i) != null) realizedCount++;
if (realizedCount != lastIndex - firstIndex + 1)
throw new InvalidOperationException("Range includes unrealized items; cannot Remove"); Type guard
static bool AllRealized(ItemContainerGenerator g, int from, int count) =>
Enumerable.Range(from, count).All(i => g.ContainerFromIndex(i) != null); Try / catch
try { generator.Remove(pos, count, true); }
catch (InvalidOperationException) { /* some items unrealized: re-derive range */ } Prevention
- Refresh positions after every CollectionChanged before recycling.
- Only recycle containers obtained from GenerateNext in the current pass.
- Clamp removal ranges to the realized window.
When it happens
Trigger: Calling Remove(position, count, ...) where the range spans unrealized (not yet generated) items — e.g. removing a batch larger than the realized region, or using a stale index after items were virtualized away.
Common situations: Custom VirtualizingPanels recycling a viewport range after a collection change shifted indices; removing a whole page of items when only part was ever generated.
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
- SR.GenerationNotInProgress
- SR.PageCacheSizeNotAllowed
- AnchorItem ' ' does not have a realized container and hence…
- Animation_Invalid_DefaultValue
- Cannot remove signature from read-only file.
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/8e13db3db5c0f5bf.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs:302
ItemBlock block;
// find the leftmost item to remove
int offsetL = index;
for (block = _itemMap.Next; block != _itemMap; block = block.Next)
{
if (offsetL < block.ContainerCount)
break;
offsetL -= block.ContainerCount;
}
RealizedItemBlock blockL = block as RealizedItemBlock;
// find the rightmost item to remove
int offsetR = offsetL + count - 1;
for (; block != _itemMap; block = block.Next)
{
if (!(block is RealizedItemBlock))
throw new InvalidOperationException(SR.Format(SR.CannotRemoveUnrealizedItems, index, count));
if (offsetR < block.ContainerCount)
break;
offsetR -= block.ContainerCount;
}
RealizedItemBlock blockR = block as RealizedItemBlock;
// de-initialize the containers that are being removed
RealizedItemBlock rblock = blockL;
int offset = offsetL;
while (rblock != blockR || offset <= offsetR)
{
DependencyObject container = rblock.ContainerAt(offset);
UnlinkContainerFromItem(container, rblock.ItemAt(offset));
// DataGrid generates non-GroupItem for NewItemPlaceHolder
// Dont recycle in this case.View on GitHub (pinned to 81131a70a4)