stride3d/stride · error · InvalidOperationException

Non-scalar key not yet supported!

Error message

Non-scalar key not yet supported!

What it means

KeyWithIdSerializer.ConvertFrom deserializes dictionary keys of KeyWithId<K,V> but only supports scalar (string-like) keys. When the key serializer for the dictionary's key type is not a ScalarSerializerBase, it throws InvalidOperationException('Non-scalar key not yet supported!'). It is a documented limitation (TODO in source).

Solutions

  1. Change the dictionary key type to a scalar (string, int, Guid, PropertyKey, etc.)
  2. Keep the complex key as a dictionary value and use a scalar identifier as the key
  3. Implement a ScalarSerializerBase for the key type so it round-trips as a scalar string
  4. Replace KeyWithId-keyed collections with lists or another supported collection shape

Example fix

// before
public DictionaryWithItemIds<MyComplexKey, int> Items { get; set; }
// after
public DictionaryWithItemIds<string, int> Items { get; set; } // use MyComplexKey.Id
Defensive patterns

Strategy: validation

Validate before calling

var keyDesc = context.FindTypeDescriptor(typeof(TKey));
var ser = context.Serializer.GetSerializer(context, keyDesc);
if (ser is not ScalarSerializerBase) throw new InvalidOperationException("Dictionary key must be scalar for KeyWithId serialization");

Type guard

bool IsScalarKey<TKey>(SerializerContext ctx) =>
    ctx.Serializer.GetSerializer(ctx, ctx.FindTypeDescriptor(typeof(TKey))) is ScalarSerializerBase;

Try / catch

try { var value = serializer.ConvertFrom(ref objectContext); }
catch (InvalidOperationException ex) { logger.Error(ex, "Non-scalar dictionary key in asset"); throw; }

Prevention

When it happens

Trigger: Deserializing an asset YAML containing a DictionaryWithItemIds (or KeyWithId keys) whose key type is a complex/non-scalar object (e.g. a struct or class key) rather than a scalar like string or int.

Common situations: Asset types using dictionaries keyed by composite objects; schema evolution where a scalar key type was changed to a complex key type; custom asset types with non-primitive dictionary keys.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/ebe4bf151e804510. Report an issue: GitHub.

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/Yaml/KeyWithIdSerializer.cs:39

    /// <inheritdoc/>
    public override object? ConvertFrom(ref ObjectContext objectContext, Scalar fromScalar)
    {
        var idIndex = fromScalar.Value.IndexOf('~');
        var id = ItemId.Empty;
        var keyString = fromScalar.Value;
        if (idIndex >= 0)
        {
            var idString = fromScalar.Value[..idIndex];
            keyString = fromScalar.Value[(idIndex + 1)..];
            id = ItemId.Parse(idString);
        }
        var keyType = objectContext.Descriptor.Type.GetGenericArguments()[0];
        var keyDescriptor = objectContext.SerializerContext.FindTypeDescriptor(keyType);
        var keySerializer = objectContext.SerializerContext.Serializer.GetSerializer(objectContext.SerializerContext, keyDescriptor);
        // TODO: deserialize non-scalar keys!
        if (keySerializer is not ScalarSerializerBase scalarKeySerializer)
            throw new InvalidOperationException("Non-scalar key not yet supported!");

        if (keyString.Length == 0)
            return Activator.CreateInstance(typeof(DeletedKeyWithId<>).MakeGenericType(keyType), id);

        var context = new ObjectContext(objectContext.SerializerContext, null, keyDescriptor);
        var key = scalarKeySerializer.ConvertFrom(ref context, new Scalar(keyString));
        var result = Activator.CreateInstance(typeof(KeyWithId<>).MakeGenericType(keyType), id, key);
        return result;
    }

    /// <inheritdoc/>
    public override string ConvertTo(ref ObjectContext objectContext)
    {
        var key = (IKeyWithId)objectContext.Instance;
        var keyDescriptor = objectContext.SerializerContext.FindTypeDescriptor(key.KeyType);
        var keySerializer = objectContext.SerializerContext.Serializer.GetSerializer(objectContext.SerializerContext, keyDescriptor);

        // TODO: serialize non-scalar keys!

View on GitHub (pinned to 96fad776d2)