{"record":{"id":"44798998357a6ece","repo":"stride3d/stride","slug":"the-order-of-the-asset-id-property-must-be-lower-than-the","errorCode":null,"errorMessage":"The order of the Asset.Id property must be lower than the order of the SourceHashes property.","messagePattern":"The order of the Asset\\.Id property must be lower than the order of the SourceHashes property\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/assets/Stride.Core.Assets/Tracking/SourceHashesHelper.cs","lineNumber":124,"sourceCode":"\n    internal class SourceHashesDynamicMember : DynamicMemberDescriptorBase\n    {\n        public const int DefaultOrder = int.MaxValue;\n\n        public static readonly SourceHashesDynamicMember Default = new()\n        {\n            ShouldSerialize = static (x, parentTypeMemberDesc) =>\n            {\n                return x is Asset asset && TryGet(asset, AbsoluteSourceHashesKey)?.Count > 0;\n            }\n        };\n\n        static SourceHashesDynamicMember()\n        {\n            // Safety check, we need to have the asset id deserialized before the source hashes\n            var idOrder = typeof(Asset).GetProperty(nameof(Asset.Id))!.GetCustomAttribute<DataMemberAttribute>()!.Order;\n            if (idOrder >= DefaultOrder)\n                throw new InvalidOperationException(\"The order of the Asset.Id property must be lower than the order of the SourceHashes property.\");\n        }\n\n        public SourceHashesDynamicMember() : base(MemberName, typeof(Dictionary<UFile, ObjectId>), typeof(Asset))\n        {\n            Order = DefaultOrder;\n        }\n\n        public override bool HasSet => true;\n\n        public override object? Get(object thisObject)\n        {\n            var asset = (Asset)thisObject;\n            // Id can be empty when the asset is contained in a base.\n            if (asset.Id == AssetId.Empty)\n                return null;\n\n            lock (LockObj)\n            {","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/assets/Stride.Core.Assets/Tracking/SourceHashesHelper.cs#L106-L142","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Restore Asset.Id's DataMember Order to a value lower than SourceHashesHelper.DefaultOrder","Renumber the DataMember Order attributes so Id serializes before SourceHashes","Update SourceHashesHelper's DefaultOrder to match your modified Asset model if the reorder was intentional","Diff your Asset.cs against upstream Stride to find the accidental Order change"],"exampleFix":"// before (Asset.cs)\n[DataMember(-8)] public AssetId Id { get; set; } // >= DefaultOrder\n// after\n[DataMember(-10)] public AssetId Id { get; set; } // lower than SourceHashes order","handlingStrategy":"try-catch","validationCode":"var idOrder = typeof(Asset).GetProperty(nameof(Asset.Id))!\n    .GetCustomAttribute<DataMemberAttribute>()!.Order;\nif (idOrder >= /* SourceHashesHelper.DefaultOrder */)\n    throw new InvalidOperationException(\"Fix Asset.Id DataMember Order before using SourceHashesHelper\");","typeGuard":null,"tryCatchPattern":"try { SourceHashesHelperTouch(); }\ncatch (TypeInitializationException ex) when (ex.InnerException is InvalidOperationException)\n{\n    Log.Fatal(\"Asset.Id DataMember Order is inconsistent with SourceHashes order: {Msg}\", ex.InnerException.Message);\n}","preventionTips":["Never renumber DataMember Order on Asset.Id casually; diff against upstream","Add a unit test that exercises SourceHashesHelper's static ctor on CI","Document the ordering invariant next to Asset.Id in forks","Re-verify the invariant after merging upstream Stride changes"],"tags":["csharp","invalidoperationexception","serialization","static-constructor"],"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-15T23:17:13.987Z"}