stride3d/stride · error · InvalidOperationException

An id is both marked as deleted and associated to a key of…

Error message

An id is both marked as deleted and associated to a key of the collection.

What it means

Thrown by CollectionItemIdentifiers.Validate when an ItemId appears both in deletedItems and as a live key's id in keyToIdMap. An id cannot simultaneously represent a deleted element and an active one; this state would corrupt id-based change tracking.

Solutions

  1. When re-adding an item whose id is in deletedItems, remove it from deletedItems or assign a brand-new ItemId.
  2. Before Validate, reconcile: for each key id present in deletedItems, either purge the deletion record or regenerate the id.
  3. Rebuild the CollectionItemIdentifiers from the current collection state to clear stale deletion markers.
  4. Fix undo/redo logic so delete-then-re-add sequences correctly restore the id.

Example fix

// before
identifiers.MarkDeleted(itemId);
identifiers["key"] = itemId; // id is both deleted and live
// after
identifiers.MarkDeleted(itemId);
identifiers.RemoveDeleted(itemId); // or assign ItemId.New()
identifiers["key"] = itemId;
Defensive patterns

Strategy: validation

Validate before calling

foreach (var key in identifiers.Keys.ToList())
{
    var id = identifiers[key];
    if (deletedIds.Contains(id))
        identifiers[key] = ItemId.New(); // revive with a fresh id
}
identifiers.Validate(isList);

Try / catch

try { identifiers.Validate(isList); }
catch (InvalidOperationException ex) when (ex.Message.Contains("deleted and associated"))
{
    // rebuild identifiers from live collection or clear stale deletions
}

Prevention

When it happens

Trigger: Calling Validate after marking an id deleted while the same id is still assigned to a key — e.g. deleting an element but not removing its entry, or re-adding an element with a previously deleted id.

Common situations: Custom collection mutation code that removes values but reuses the deleted id; undo/redo implementations replaying add/delete in the wrong order; merging changes where a deleted item was re-added with its old id.

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 stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/2c176f1077cf8508. Report an issue: GitHub.

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/Reflection/CollectionItemIdentifiers.cs:129

        deletedItems.Add(id);
    }

    public void UnmarkAsDeleted(ItemId id)
    {
        deletedItems.Remove(id);
    }

    public void Validate(bool isList)
    {
        var ids = new HashSet<ItemId>(keyToIdMap.Values);
        if (ids.Count != keyToIdMap.Count)
            throw new InvalidOperationException("Two elements of the collection have the same id");

        foreach (var deleted in deletedItems)
            ids.Add(deleted);

        if (ids.Count != keyToIdMap.Count + deletedItems.Count)
            throw new InvalidOperationException("An id is both marked as deleted and associated to a key of the collection.");
    }

    public object? GetKey(ItemId itemId)
    {
        object? output = null;
        // TODO: add indexing by guid to avoid O(n)
        foreach (var kvp in keyToIdMap)
        {
            if (kvp.Value == itemId)
            {
                if(output != null)
                    throw new InvalidOperationException("Two elements of the collection have the same id");
                output = kvp.Key;
            }
        }
        return output;
    }

View on GitHub (pinned to 96fad776d2)