stride3d/stride · error · InvalidOperationException

Event handlers can't be added or removed after the…

Error message

Event handlers can't be added or removed after the serializer has been initialized.

What it means

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.

Solutions

  1. Subscribe to PrepareMembers immediately after constructing AssetYamlSerializer, before any serialize/deserialize call
  2. Create a fresh AssetYamlSerializer instance for code that needs its own PrepareMembers handler
  3. Hoist subscription to application startup before any asset I/O
  4. Refactor to pass member-preparation behavior via serializer settings instead of the event

Example fix

// before
var assets = serializer.Deserialize(stream); // initializes serializer
serializer.PrepareMembers += MyHandler; // throws
// after
serializer.PrepareMembers += MyHandler; // subscribe first
var assets = serializer.Deserialize(stream);
Defensive patterns

Strategy: validation

Validate before calling

// subscribe only if the serializer has never been used
if (!serializerUsed)
    serializer.PrepareMembers += MyHandler;

Try / catch

try { serializer.PrepareMembers += MyHandler; }
catch (InvalidOperationException)
{
    serializer = new AssetYamlSerializer();
    serializer.PrepareMembers += MyHandler;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/Yaml/AssetYamlSerializer.cs:28

namespace Stride.Core.Yaml;

/// <summary>
/// Default Yaml serializer used to serialize assets by default.
/// </summary>
public class AssetYamlSerializer : YamlSerializerBase
{
    private event Action<ObjectDescriptor, List<IMemberDescriptor>>? PrepareMembersEvent;

    private Serializer? serializer;

    public static AssetYamlSerializer Default { get; set; } = new AssetYamlSerializer();

    public event Action<ObjectDescriptor, List<IMemberDescriptor>> PrepareMembers
    {
        add
        {
            if (serializer != null)
                throw new InvalidOperationException("Event handlers can't be added or removed after the serializer has been initialized.");

            PrepareMembersEvent += value;
        }
        remove
        {
            if (serializer != null)
                throw new InvalidOperationException("Event handlers can't be added or removed after the serializer has been initialized.");
            PrepareMembersEvent -= value;
        }
    }

    /// <summary>
    /// Deserializes an object from the specified stream (expecting a YAML string).
    /// </summary>
    /// <param name="stream">A YAML string from a stream.</param>
    /// <param name="expectedType">The expected type.</param>
    /// <param name="contextSettings">The context settings.</param>
    /// <returns>An instance of the YAML data.</returns>

View on GitHub (pinned to 96fad776d2)