dotnet/wpf · critical · InvalidOperationException

SR.Generator_Inconsistent

Error message

SR.Generator_Inconsistent

What it means

ItemContainerGenerator periodically validates that its internal item map is consistent with the Items collection (VerifyItemMapAndSource). When a mismatch is detected it builds a detailed diagnostic string, wraps it in an inner Exception, and throws InvalidOperationException SR.Generator_Inconsistent to report a broken generator state.

Solutions

  1. Fix the source collection so its CollectionChanged notifications exactly match its contents (each Add/Remove reflects real items).
  2. Implement INotifyCollectionChanged correctly on custom collections, or wrap with ObservableCollection.
  3. Never mutate the Items collection from multiple threads or re-entrantly while CollectionChanged handlers run.

Example fix

// before
class BuggyCollection : List<object>, INotifyCollectionChanged { /* raises events inconsistently */ }

// after
class FixedCollection : ObservableCollection<object> { } // notifications always match contents
Defensive patterns

Strategy: try-catch

Try / catch

try { /* bind / refresh ItemsSource */ }
catch (InvalidOperationException ex) when (ex.InnerException != null && ex.Message.Contains("inconsistent"))
{
    // inspect ex.InnerException for the detailed map dump, then force refresh
    itemsControl.Items.Refresh();
}

Prevention

When it happens

Trigger: The Items collection was mutated in ways the generator could not track — e.g. raising CollectionChanged events that do not match the actual collection contents, direct mutation of ItemsInternal without notifications, or custom IList implementations raising inconsistent Add/Remove events.

Common situations: Custom collection classes with buggy INotifyCollectionChanged implementations; modifying ItemsSource collection from background threads; re-entrancy during collection change notifications.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/cf21c32010065343. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs:1101

                sb.AppendLine();

                sb.AppendLine(SR.Generator_Readme5);                          // The most common causes...
                sb.AppendLine();

                sb.Append    (SR.Generator_Readme6); sb.Append("  ");         // Stack trace describes detection...
                sb.Append    (SR.Format(SR.Generator_Readme7,                            // To get better detection...
                                "PresentationTraceSources.TraceLevel", "High"));
                sb.Append    ("  ");
                sb.AppendLine(SR.Format(SR.Generator_Readme8,                            // One way to do this ...
                                "System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator, System.Diagnostics.PresentationTraceLevel.High)"));
                sb.AppendLine(SR.Generator_Readme9);                          // This slows down the app.

                // use an inner exception to hold the details.  There's a lot of
                // information, but it's only interesting to a developer.
                Exception exception = new Exception(sb.ToString());

                // throw the exception
                throw new InvalidOperationException(SR.Generator_Inconsistent, exception);
            }
        }

        private void FormatCollectionChangedSource(int level, object source, bool? isLikely, List<string> sources)
        {
            Type sourceType = source.GetType();

            if (!isLikely.HasValue)
            {
                // if the type doesn't come from WPF or DevDiv (e.g. ObservableCollection<T>),
                // mark it as "more likely to be at fault".   I'm not saying we're always right,
                // just that 3rd parties are more likely to be wrong than we are.
                isLikely = true;

                const string PublicKeyToken = "PublicKeyToken=";
                string aqn = sourceType.AssemblyQualifiedName;
                int index = aqn.LastIndexOf(PublicKeyToken);
                if (index >= 0)

View on GitHub (pinned to 81131a70a4)