{"record":{"id":"21eda938b33bdc9f","repo":"stride3d/stride","slug":"cannot-add-a-factory-to-a-serializer-factory-selector-once","errorCode":null,"errorMessage":"Cannot add a factory to a serializer factory selector once it is sealed.","messagePattern":"Cannot add a factory to a serializer factory selector once it is sealed\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.Yaml/Serialization/SerializerFactorySelector.cs","lineNumber":24,"sourceCode":"using Stride.Core.Reflection;\n\nnamespace Stride.Core.Yaml.Serialization\n{\n    /// <summary>\n    /// Base class that implements <see cref=\"ISerializerFactorySelector\"/>.\n    /// </summary>\n    public abstract class SerializerFactorySelector : ISerializerFactorySelector\n    {\n        private readonly Dictionary<Type, IYamlSerializable> serializers = new Dictionary<Type, IYamlSerializable>();\n        private readonly List<IYamlSerializableFactory> factories = new List<IYamlSerializableFactory>();\n        private readonly ReaderWriterLockSlim serializerLock = new ReaderWriterLockSlim();\n        private bool isSealed;\n\n        /// <inheritdoc/>\n        public void TryAddFactory(IYamlSerializableFactory factory)\n        {\n            if (factory == null) throw new ArgumentNullException(nameof(factory));\n            if (isSealed) throw new InvalidOperationException(\"Cannot add a factory to a serializer factory selector once it is sealed.\");\n            if (CanAddSerializerFactory(factory))\n            {\n                factories.Add(factory);\n            }\n        }\n\n        /// <inheritdoc/>\n        public void Seal()\n        {\n            isSealed = true;\n        }\n\n        /// <inheritdoc/>\n        public IYamlSerializable GetSerializer(SerializerContext context, ITypeDescriptor typeDescriptor)\n        {\n            if (!isSealed) throw new InvalidOperationException(\"A serializer factory selector must be sealed before being used.\");\n            IYamlSerializable serializer;\n","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Yaml/Serialization/SerializerFactorySelector.cs#L6-L42","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Register all factories before Seal()/first use; seal only after registration completes.","Restructure lazy plugin loading to happen at initialization time.","Give each subsystem its own SerializerFactorySelector instead of sharing a sealed one.","Catch InvalidOperationException and treat it as a configuration-order bug (log it).","Check an isSealed flag of your own before calling TryAddFactory."],"exampleFix":"// before\nselector.Seal();\nselector.TryAddFactory(myFactory); // throws\n// after\nselector.TryAddFactory(myFactory);\nselector.Seal();","handlingStrategy":"validation","validationCode":"// track sealing yourself\nif (sealedFlag) throw new InvalidOperationException(\"Serializer configuration is closed; register factories earlier\");","typeGuard":null,"tryCatchPattern":"try { selector.TryAddFactory(factory); }\ncatch (InvalidOperationException ex) { log.Error(\"Factory registration after seal — restructure initialization\", ex); throw; }","preventionTips":["Complete all factory registration before calling Seal()","Never lazily register factories during first deserialization","Avoid sharing a sealed selector across independently configured subsystems","Treat Seal() as the end of a dedicated init phase"],"tags":["yaml","invalid-operation","serializer-factory"],"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"}