{"record":{"id":"7c4de75808acb670","repo":"stride3d/stride","slug":"a-materialpass-can-only-belong-to-a-single-material","errorCode":null,"errorMessage":"A MaterialPass can only belong to a single Material","messagePattern":"A MaterialPass can only belong to a single Material","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Rendering/Rendering/MaterialPassCollection.cs","lineNumber":26,"sourceCode":"{\n    /// <summary>\n    /// A collection of <see cref=\"MaterialPass\"/>.\n    /// </summary>\n    [DataSerializer(typeof(ListAllSerializer<MaterialPassCollection, MaterialPass>))]\n    public sealed class MaterialPassCollection : FastCollection<MaterialPass>\n    {\n        private readonly Material material;\n\n        internal MaterialPassCollection(Material material)\n        {\n            this.material = material;\n        }\n\n        /// <inheritdoc/>\n        protected override void InsertItem(int index, MaterialPass item)\n        {\n            if (item.Material != null)\n                throw new InvalidOperationException($\"A {nameof(MaterialPass)} can only belong to a single {nameof(Material)}\");\n\n            base.InsertItem(index, item);\n            item.Material = material;\n        }\n\n        /// <inheritdoc/>\n        protected override void RemoveItem(int index)\n        {\n            this[index].Material = null;\n            base.RemoveItem(index);\n        }\n\n        /// <inheritdoc/>\n        protected override void SetItem(int index, MaterialPass item)\n        {\n            // Note: Changing CollectionChanged is not thread-safe\n            var oldItem = this[index];\n            if (oldItem != null)","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Rendering/Rendering/MaterialPassCollection.cs#L8-L44","documentation":"MaterialPassCollection.InsertItem enforces that each MaterialPass belongs to exactly one Material. When inserting a pass whose Material property is already set to another material, it throws InvalidOperationException to prevent sharing a pass between materials.","triggerScenarios":"Adding the same MaterialPass instance (e.g. grabbed from another material's Passes collection or reused from a cached pass) into a second material's Passes list.","commonSituations":"Sharing a pass between two materials to save memory; caching passes and re-adding them to newly built materials; copying passes by reference instead of cloning.","solutions":["Create a new MaterialPass instance for the second material instead of reusing the pass","If sharing is intentional, clone the pass and copy its relevant properties","Clear the pass's Material reference before reassigning only if you truly intend to move it (remove it from the first material first)"],"exampleFix":"// before: reusing the same pass in two materials\nmaterialA.Passes.Add(pass);\nmaterialB.Passes.Add(pass); // throws\n// after: give each material its own pass\nmaterialA.Passes.Add(pass);\nmaterialB.Passes.Add(new MaterialPass { BlendStates = pass.BlendStates, ... });","handlingStrategy":"validation","validationCode":"if (pass.Material != null && pass.Material != targetMaterial)\n    pass = ClonePass(pass); // create a copy before adding\ntargetMaterial.Passes.Add(pass);","typeGuard":"bool IsUnowned(MaterialPass p) => p.Material == null;","tryCatchPattern":null,"preventionTips":["Never reuse MaterialPass instances across materials","Clone passes when sharing settings","Remove a pass from its owner before moving it"],"tags":["material","materialpass","ownership","csharp"],"backgroundTag":"invalid-argument-value","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"}