{"record":{"id":"4940d3de2d266a28","repo":"stride3d/stride","slug":"cannot-modify-a-yamlassetmetadata-after-it-has-been-attached","errorCode":null,"errorMessage":"Cannot modify a YamlAssetMetadata after it has been attached.","messagePattern":"Cannot modify a YamlAssetMetadata after it has been attached\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/assets/Stride.Core.Assets/Yaml/YamlAssetMetadata.cs","lineNumber":55,"sourceCode":"public class YamlAssetMetadata<T> : IYamlAssetMetadata, IEnumerable<KeyValuePair<YamlAssetPath, T>>\n{\n\n    private readonly Dictionary<YamlAssetPath, T> metadata = new(YamlAssetPathComparer.Default);\n    private bool isAttached;\n\n    /// <summary>\n    /// Gets the number of key/value pairs contained in the <see cref=\"YamlAssetMetadata{T}\"/>\n    /// </summary>\n    public int Count => metadata.Count;\n\n    /// <summary>\n    /// Attaches the given metadata value to the given YAML path.\n    /// </summary>\n    /// <param name=\"path\">The path at which to attach metadata.</param>\n    /// <param name=\"value\">The metadata to attach.</param>\n    public void Set(YamlAssetPath path, T value)\n    {\n        if (isAttached) throw new InvalidOperationException(\"Cannot modify a YamlAssetMetadata after it has been attached.\");\n        metadata[path] = value;\n    }\n\n    /// <summary>\n    /// Removes attached metadata from the given YAML path.\n    /// </summary>\n    /// <param name=\"path\">The path at which to remove metadata.</param>\n    public void Remove(YamlAssetPath path)\n    {\n        if (isAttached) throw new InvalidOperationException(\"Cannot modify a YamlAssetMetadata after it has been attached.\");\n        metadata.Remove(path);\n    }\n\n    /// <summary>\n    /// Tries to retrieve the metadata for the given path.\n    /// </summary>\n    /// <param name=\"path\">The path at which to retrieve metadata.</param>\n    /// <returns>The metadata attached to the given path, or the default value of <typeparamref name=\"T\"/> if no metadata is attached at the given path.</returns>","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/assets/Stride.Core.Assets/Yaml/YamlAssetMetadata.cs#L37-L73","documentation":"YamlAssetMetadata.Set attaches metadata to a YAML path, but once the metadata object has been 'attached' (e.g. during asset processing/fixup), it becomes read-only. Any Set after attachment throws InvalidOperationException to protect invariants during path remapping.","triggerScenarios":"Calling Set on a YamlAssetMetadata<T> after Attach has run, e.g. during RemapIdentifiablePaths or in tests after the metadata was passed to the asset-processing pipeline.","commonSituations":"Game-editors/plugins trying to mutate asset metadata after loading/processing; unit tests caching a shared YamlAssetMetadata instance that was already used in serialization; two-phase asset import code that adds metadata late.","solutions":["Perform all Set calls before passing the metadata to Attach/asset processing","Create a new YamlAssetMetadata<T> instance and copy values if post-attach mutation is needed","Check isAttached (or expose an accessor) before mutating, and defer attachment until all writes are done","In remapping code, build a fresh metadata dictionary and attach it once at the end"],"exampleFix":"// before\nvar meta = LoadAssetMetadata(); // already attached\nmeta.Set(path, value); // throws\n// after\nvar meta = new YamlAssetMetadata<object>();\nmeta.Set(path, value);\nmeta.Attach(...);","handlingStrategy":"type-guard","validationCode":"if (metadata.IsAttached) throw new InvalidOperationException(\"Attach metadata only after all Set calls\");","typeGuard":"bool CanEdit<T>(YamlAssetMetadata<T> m) => !m.IsAttached; // expose or mirror isAttached","tryCatchPattern":"try { metadata.Set(path, value); }\ncatch (InvalidOperationException ex) { logger.Warn(ex, \"Metadata already attached; change dropped\"); }","preventionTips":["Batch all metadata writes before attaching","Never cache shared YamlAssetMetadata instances across loads","Copy entries into a new instance if post-attach edits are needed"],"tags":["yaml","metadata","immutability","invalid-state"],"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"}