stride3d/stride · error · KeyNotFoundException

Unable to find the non-value type key in the dictionary of c

Error message

Unable to find the non-value type key in the dictionary of cloned keys.

What it means

CollectionItemIdentifiers.CloneInto copies the identifier state into a target instance. For reference-type keys it relies on a dictionary of previously cloned keys (referenceTypeClonedKeys) populated during the clone session; when it encounters a key that was never registered there, it cannot map the key into the clone and throws KeyNotFoundException. This prevents silently creating a clone whose items reference untracked keys.

Solutions

  1. Ensure every item is cloned via the same CollectionItemIdentifiers instance's Clone before CloneInto, so all reference-type keys are registered in referenceTypeClonedKeys.
  2. Re-run the full clone operation (fresh Clone followed immediately by CloneInto) instead of reusing a stale clone-state object.
  3. Verify no items were added or removed between Clone and CloneInto; snapshot the collection first.
  4. As a workaround, construct a new CollectionItemIdentifiers and re-add each item with a fresh id rather than patching clone state.

Example fix

// before: items added after Clone are unknown to referenceTypeClonedKeys
var cloneState = identifiers.Clone();
identifiers.Add(ItemId.New(), newItem);
identifiers.CloneInto(cloneState);

// after: add items before cloning, or start a fresh clone session
identifiers.Add(ItemId.New(), newItem);
var cloneState = identifiers.Clone();
identifiers.CloneInto(cloneState);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!identifiers.TryGetValue(key.Key, out _))
    throw new InvalidOperationException($"Key {key.Key} was not registered by Clone before CloneInto");

Try / catch

try { identifiers.CloneInto(target); }
catch (KeyNotFoundException) { identifiers = new CollectionItemIdentifiers(); /* restart clone session */ }

Prevention

When it happens

Trigger: Calling CloneInto when the source collection contains an object key that was never passed through Clone (so referenceTypeClonedKeys has no entry for it) — e.g. items added to the collection after cloning began, or keys shared across multiple collections where only one was cloned.

Common situations: Deep-cloning a package/asset whose identified collection gained new entries between the Clone and CloneInto calls; cloning two collections that share the same key objects; partially completed clone workflows in editor tooling.

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/bcb3918829c32eab. Report an issue: GitHub.

Appendix: source

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

    }

    public void CloneInto(CollectionItemIdentifiers target, IReadOnlyDictionary<object, object>? referenceTypeClonedKeys)
    {
        target.keyToIdMap.Clear();
        target.deletedItems.Clear();
        foreach (var key in keyToIdMap)
        {
            if (key.Key.GetType().IsValueType || referenceTypeClonedKeys == null)
            {
                target.Add(key.Key, key.Value);
            }
            else if (referenceTypeClonedKeys.TryGetValue(key.Key, out var clonedKey))
            {
                target.Add(clonedKey, key.Value);
            }
            else
            {
                throw new KeyNotFoundException("Unable to find the non-value type key in the dictionary of cloned keys.");
            }
        }
        foreach (var deletedItem in DeletedItems)
        {
            target.MarkAsDeleted(deletedItem);
        }
    }

    public bool IsDeleted(ItemId itemId)
    {
        return DeletedItems.Contains(itemId);
    }

    public IEnumerator<KeyValuePair<object, ItemId>> GetEnumerator() => keyToIdMap.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

View on GitHub (pinned to 96fad776d2)