stride3d/stride · error · InvalidOperationException

The order of the Asset.Id property must be lower than the…

Error message

The order of the Asset.Id property must be lower than the order of the SourceHashes property.

What it means

SourceHashesHelper's static constructor verifies serialization ordering invariants: Asset.Id (its DataMember Order) must be strictly lower than the SourceHashes member order (DefaultOrder), so the asset id is always deserialized before the source hashes. If the Asset metadata model changes those Order values, the static initializer throws InvalidOperationException at first use.

Solutions

  1. Restore Asset.Id's DataMember Order to a value lower than SourceHashesHelper.DefaultOrder
  2. Renumber the DataMember Order attributes so Id serializes before SourceHashes
  3. Update SourceHashesHelper's DefaultOrder to match your modified Asset model if the reorder was intentional
  4. Diff your Asset.cs against upstream Stride to find the accidental Order change

Example fix

// before (Asset.cs)
[DataMember(-8)] public AssetId Id { get; set; } // >= DefaultOrder
// after
[DataMember(-10)] public AssetId Id { get; set; } // lower than SourceHashes order
Defensive patterns

Strategy: try-catch

Validate before calling

var idOrder = typeof(Asset).GetProperty(nameof(Asset.Id))!
    .GetCustomAttribute<DataMemberAttribute>()!.Order;
if (idOrder >= /* SourceHashesHelper.DefaultOrder */)
    throw new InvalidOperationException("Fix Asset.Id DataMember Order before using SourceHashesHelper");

Try / catch

try { SourceHashesHelperTouch(); }
catch (TypeInitializationException ex) when (ex.InnerException is InvalidOperationException)
{
    Log.Fatal("Asset.Id DataMember Order is inconsistent with SourceHashes order: {Msg}", ex.InnerException.Message);
}

Prevention

When it happens

Trigger: Modifying Asset.Id's DataMember Order in a fork/custom build so it is >= DefaultOrder; reordering/renumbering DataMember Order attributes on Asset properties; upgrading a patched Stride source where the safety check no longer holds.

Common situations: Custom engine builds that add new serialized Asset properties and shift Order values; merging upstream Stride changes that renumber DataMember orders; copy-pasting Asset into a custom asset pipeline.

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

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/Tracking/SourceHashesHelper.cs:124

    internal class SourceHashesDynamicMember : DynamicMemberDescriptorBase
    {
        public const int DefaultOrder = int.MaxValue;

        public static readonly SourceHashesDynamicMember Default = new()
        {
            ShouldSerialize = static (x, parentTypeMemberDesc) =>
            {
                return x is Asset asset && TryGet(asset, AbsoluteSourceHashesKey)?.Count > 0;
            }
        };

        static SourceHashesDynamicMember()
        {
            // Safety check, we need to have the asset id deserialized before the source hashes
            var idOrder = typeof(Asset).GetProperty(nameof(Asset.Id))!.GetCustomAttribute<DataMemberAttribute>()!.Order;
            if (idOrder >= DefaultOrder)
                throw new InvalidOperationException("The order of the Asset.Id property must be lower than the order of the SourceHashes property.");
        }

        public SourceHashesDynamicMember() : base(MemberName, typeof(Dictionary<UFile, ObjectId>), typeof(Asset))
        {
            Order = DefaultOrder;
        }

        public override bool HasSet => true;

        public override object? Get(object thisObject)
        {
            var asset = (Asset)thisObject;
            // Id can be empty when the asset is contained in a base.
            if (asset.Id == AssetId.Empty)
                return null;

            lock (LockObj)
            {

View on GitHub (pinned to 96fad776d2)