{"record":{"id":"68a565333b868a2b","repo":"stride3d/stride","slug":"event-handlers-can-t-be-added-or-removed-after-the","errorCode":null,"errorMessage":"Event handlers can't be added or removed after the serializer has been initialized.","messagePattern":"Event handlers can't be added or removed after the serializer has been initialized\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/assets/Stride.Core.Assets/Yaml/AssetYamlSerializer.cs","lineNumber":28,"sourceCode":"namespace Stride.Core.Yaml;\n\n/// <summary>\n/// Default Yaml serializer used to serialize assets by default.\n/// </summary>\npublic class AssetYamlSerializer : YamlSerializerBase\n{\n    private event Action<ObjectDescriptor, List<IMemberDescriptor>>? PrepareMembersEvent;\n\n    private Serializer? serializer;\n\n    public static AssetYamlSerializer Default { get; set; } = new AssetYamlSerializer();\n\n    public event Action<ObjectDescriptor, List<IMemberDescriptor>> PrepareMembers\n    {\n        add\n        {\n            if (serializer != null)\n                throw new InvalidOperationException(\"Event handlers can't be added or removed after the serializer has been initialized.\");\n\n            PrepareMembersEvent += value;\n        }\n        remove\n        {\n            if (serializer != null)\n                throw new InvalidOperationException(\"Event handlers can't be added or removed after the serializer has been initialized.\");\n            PrepareMembersEvent -= value;\n        }\n    }\n\n    /// <summary>\n    /// Deserializes an object from the specified stream (expecting a YAML string).\n    /// </summary>\n    /// <param name=\"stream\">A YAML string from a stream.</param>\n    /// <param name=\"expectedType\">The expected type.</param>\n    /// <param name=\"contextSettings\">The context settings.</param>\n    /// <returns>An instance of the YAML data.</returns>","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/assets/Stride.Core.Assets/Yaml/AssetYamlSerializer.cs#L10-L46","documentation":"AssetYamlSerializer wraps an underlying YamlSerializer that is created lazily; once it exists (serializer != null), the PrepareMembers event can no longer be subscribed. The add accessor throws InvalidOperationException so late subscribers cannot silently miss callbacks that already ran during initialization.","triggerScenarios":"Subscribing serializer.PrepareMembers += handler after any Serialize/Deserialize/GetSerializerSettings call has initialized the inner serializer; using a shared/singleton serializer instance whose first use happened elsewhere; subscribing from a different subsystem after app startup.","commonSituations":"Editor plugins that hook serialization after the editor already loaded assets; tests where one test serialized with the shared serializer and a later test tries to attach a handler; DI singletons shared across modules.","solutions":["Subscribe to PrepareMembers immediately after constructing AssetYamlSerializer, before any serialize/deserialize call","Create a fresh AssetYamlSerializer instance for code that needs its own PrepareMembers handler","Hoist subscription to application startup before any asset I/O","Refactor to pass member-preparation behavior via serializer settings instead of the event"],"exampleFix":"// before\nvar assets = serializer.Deserialize(stream); // initializes serializer\nserializer.PrepareMembers += MyHandler; // throws\n// after\nserializer.PrepareMembers += MyHandler; // subscribe first\nvar assets = serializer.Deserialize(stream);","handlingStrategy":"validation","validationCode":"// subscribe only if the serializer has never been used\nif (!serializerUsed)\n    serializer.PrepareMembers += MyHandler;","typeGuard":null,"tryCatchPattern":"try { serializer.PrepareMembers += MyHandler; }\ncatch (InvalidOperationException)\n{\n    serializer = new AssetYamlSerializer();\n    serializer.PrepareMembers += MyHandler;\n}","preventionTips":["Subscribe to PrepareMembers at construction time, before any Serialize/Deserialize call","Avoid sharing one AssetYamlSerializer singleton across modules that need distinct handlers","Track whether the serializer has been used and assert before late subscription","Move member customization into startup composition code"],"tags":["csharp","invalidoperationexception","yaml","serialization","event-lifecycle"],"backgroundTag":"invalid-state-transition","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"}