stride3d/stride · error · InvalidOperationException

Cannot add a factory to a serializer factory selector once…

Error message

Cannot add a factory to a serializer factory selector once it is sealed.

What it means

TryAddFactory throws InvalidOperationException once the selector has been sealed (Seal() sets isSealed). Sealing is a one-way transition: after it, the set of serializer factories is frozen so GetSerializer can safely cache results; late additions are rejected to protect the caching invariant.

Solutions

  1. Register all factories before Seal()/first use; seal only after registration completes.
  2. Restructure lazy plugin loading to happen at initialization time.
  3. Give each subsystem its own SerializerFactorySelector instead of sharing a sealed one.
  4. Catch InvalidOperationException and treat it as a configuration-order bug (log it).
  5. Check an isSealed flag of your own before calling TryAddFactory.

Example fix

// before
selector.Seal();
selector.TryAddFactory(myFactory); // throws
// after
selector.TryAddFactory(myFactory);
selector.Seal();
Defensive patterns

Strategy: validation

Validate before calling

// track sealing yourself
if (sealedFlag) throw new InvalidOperationException("Serializer configuration is closed; register factories earlier");

Try / catch

try { selector.TryAddFactory(factory); }
catch (InvalidOperationException ex) { log.Error("Factory registration after seal — restructure initialization", ex); throw; }

Prevention

When it happens

Trigger: Calling TryAddFactory after Seal() was invoked — e.g. registering factories lazily while deserialization already started, or two components both configuring a shared serializer with one sealing early.

Common situations: Plugin systems that add custom serializers on first use; race conditions where one thread begins deserializing (sealing implicitly) while another registers; static/shared serializer instances configured in multiple places.

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/21eda938b33bdc9f. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.Yaml/Serialization/SerializerFactorySelector.cs:24

using Stride.Core.Reflection;

namespace Stride.Core.Yaml.Serialization
{
    /// <summary>
    /// Base class that implements <see cref="ISerializerFactorySelector"/>.
    /// </summary>
    public abstract class SerializerFactorySelector : ISerializerFactorySelector
    {
        private readonly Dictionary<Type, IYamlSerializable> serializers = new Dictionary<Type, IYamlSerializable>();
        private readonly List<IYamlSerializableFactory> factories = new List<IYamlSerializableFactory>();
        private readonly ReaderWriterLockSlim serializerLock = new ReaderWriterLockSlim();
        private bool isSealed;

        /// <inheritdoc/>
        public void TryAddFactory(IYamlSerializableFactory factory)
        {
            if (factory == null) throw new ArgumentNullException(nameof(factory));
            if (isSealed) throw new InvalidOperationException("Cannot add a factory to a serializer factory selector once it is sealed.");
            if (CanAddSerializerFactory(factory))
            {
                factories.Add(factory);
            }
        }

        /// <inheritdoc/>
        public void Seal()
        {
            isSealed = true;
        }

        /// <inheritdoc/>
        public IYamlSerializable GetSerializer(SerializerContext context, ITypeDescriptor typeDescriptor)
        {
            if (!isSealed) throw new InvalidOperationException("A serializer factory selector must be sealed before being used.");
            IYamlSerializable serializer;

View on GitHub (pinned to 96fad776d2)