{"record":{"id":"08810cfbd72ca2d1","repo":"stride3d/stride","slug":"multiple-items-in-the-collection-are-changing-concurrently","errorCode":null,"errorMessage":"Multiple items in the collection are changing concurrently.","messagePattern":"Multiple items in the collection are changing concurrently\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/presentation/Stride.Core.Presentation/Collections/AutoUpdatingSortedObservableCollection.cs","lineNumber":114,"sourceCode":"                propertyNames.Add(name);\n            }\n        }\n    }\n\n    /// <inheritdoc/>\n    public override string ToString()\n    {\n        return $\"{{AutoUpdatingSortedObservableCollection}} Count = {Count}\";\n    }\n\n    protected virtual void ItemPropertyChanging(object? sender, PropertyChangingEventArgs e)\n    {\n        if (e.PropertyName is null || propertyNames?.Contains(e.PropertyName) == false)\n            return;\n\n        var item = (T?)sender;\n        if (ChangingItem is not null && !ReferenceEquals(ChangingItem, item))\n            throw new InvalidOperationException(\"Multiple items in the collection are changing concurrently.\");\n\n        ++changeCount;\n\n        ChangingItem = item;\n        ChangingIndex = GetIndex(item, false);\n        AddedItem = null;\n    }\n\n    protected virtual void ItemPropertyChanged(object? sender, PropertyChangedEventArgs e)\n    {\n        if (e.PropertyName is null || propertyNames?.Contains(e.PropertyName) == false)\n            return;\n\n        var item = (T?)sender;\n\n        // An object has been added while a property of an existing object has been modified\n        if (ChangingItem is not null && AddedItem is not null)\n            throw new InvalidOperationException(\"PropertyChanged is invoked without PropertyChanging, or multiple items in the collection are changing concurrently.\");","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/presentation/Stride.Core.Presentation/Collections/AutoUpdatingSortedObservableCollection.cs#L96-L132","documentation":"AutoUpdatingSortedObservableCollection subscribes to each item's PropertyChanging to preregister the changing item and its index for later reordering. If ItemPropertyChanging fires for a second item while ChangingItem still refers to a different item (no matching ItemPropertyChanged in between), the internal change-tracking invariant is broken and it throws.","triggerScenarios":"Nested property changes on two different items within one PropertyChanging/PropertyChanged cycle, e.g. an item's property setter mutating another item already in the collection; exceptions in property setters that prevent PropertyChanged from firing.","commonSituations":"Interdependent view-models where one object's property change cascades into another's; batch updates that mutate several items inside one change scope; buggy INotifyPropertyChanged implementations that raise Changed without Changing.","solutions":["Ensure every PropertyChanging is paired with exactly one PropertyChanged on the same object, with no interleaved changes to other items","Fix item property setters that mutate other collection items while a change is in progress","Wrap multi-item mutations so each item's change completes before the next starts","Add a try/finally in setters so PropertyChanged fires even if intermediate code throws"],"exampleFix":"// before\npublic int Score\n{\n    get => score;\n    set { score = value; neighbor.Score = value; } // cascades into another item mid-change\n}\n// after\npublic int Score\n{\n    get => score;\n    set { SetProperty(ref score, value); } // cascade done from outside, sequentially\n}","handlingStrategy":"validation","validationCode":"// Audit item setters: ensure no setter mutates another item of the same collection while raising PropertyChanging/PropertyChanged","typeGuard":null,"tryCatchPattern":"try { collection.UpdateItems(...); } catch (InvalidOperationException ex) { logger.Error(ex, \"interleaved item change\"); }","preventionTips":["Keep property setters self-contained — no cascading mutations of sibling items","Always pair PropertyChanging with PropertyChanged","Use try/finally so Changed fires even if setter code throws"],"tags":["collections","inotifypropertychanged","concurrency"],"backgroundTag":"internal-invariant-violation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}