{"record":{"id":"74170def00b98c27","repo":"stride3d/stride","slug":"destroying-a-blob-not-created-through-objectdatabase","errorCode":null,"errorMessage":"Destroying a blob not created through ObjectDatabase.CreateBlob.","messagePattern":"Destroying a blob not created through ObjectDatabase\\.CreateBlob\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.Serialization/Storage/ObjectDatabase.cs","lineNumber":474,"sourceCode":"\n                // Dispose the previously opened stream.\n                stream.Dispose();\n            }\n\n            // Lookup adds a reference\n            blob.AddReference();\n        }\n\n        return blob;\n    }\n\n    internal static void DestroyBlob(Blob blob)\n    {\n        // Remove blob from cache when destroyed\n        lock (LoadedBlobs)\n        {\n            if (!LoadedBlobs.Remove(blob.ObjectId))\n                throw new InvalidOperationException(\"Destroying a blob not created through ObjectDatabase.CreateBlob.\");\n        }\n    }\n\n    private Stream OpenStreamForRead(ObjectId objectId, VirtualFileMode mode, VirtualFileAccess access, VirtualFileShare share)\n    {\n        if (BundleBackend?.Exists(objectId) == true)\n            return BundleBackend.OpenStream(objectId, mode, access, share);\n\n        if (backendRead1.Exists(objectId))\n            return backendRead1.OpenStream(objectId, mode, access, share);\n\n        if (backendRead2?.Exists(objectId) == true)\n            return backendRead2.OpenStream(objectId, mode, access, share);\n\n        throw new FileNotFoundException();\n    }\n}\n","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Serialization/Storage/ObjectDatabase.cs#L456-L492","documentation":"DestroyBlob removes a blob from the LoadedBlobs cache and is only valid for blobs produced by ObjectDatabase.CreateBlob. If the blob's ObjectId is not present in the cache, the blob was created elsewhere (or already destroyed), so the throw guards against double-free/foreign-blob disposal of unmanaged memory.","triggerScenarios":"Calling ObjectDatabase.DestroyBlob on a manually constructed Blob (not from CreateBlob), or calling it twice on the same blob (second call finds it already removed).","commonSituations":"Custom asset loaders creating Blobs by hand then disposing via DestroyBlob; lifecycle bugs where blob destruction runs twice (e.g. both Dispose and finalizer path); blobs loaded from a different ObjectDatabase instance than the one destroying them (separate LoadedBlobs cache).","solutions":["Only destroy blobs obtained from ObjectDatabase.CreateBlob on the same ObjectDatabase instance.","Ensure destruction is idempotent — check/dispose once (guard with a flag or null out references).","Use Blob.Dispose rather than DestroyBlob for blobs you did not create via CreateBlob."],"exampleFix":"// before\nvar blob = new Blob(objectId, data); // manually created\nObjectDatabase.DestroyBlob(blob);\n// after\nvar blob = ObjectDatabase.CreateBlob(stream); // created through the API\nObjectDatabase.DestroyBlob(blob);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"static bool CanDestroy(Blob blob) =>\n    ObjectDatabase.LoadedBlobs != null && blob != null && ObjectDatabase.LoadedBlobs.ContainsKey(blob.ObjectId);","tryCatchPattern":"try { ObjectDatabase.DestroyBlob(blob); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Destroying a blob not created\")) {\n    // blob was foreign or already destroyed; fall back to Dispose\n    blob?.Dispose();\n}","preventionTips":["Only destroy blobs returned by CreateBlob from the same ObjectDatabase instance","Make blob teardown idempotent (null references after destruction)","Prefer Blob.Dispose for general cleanup"],"tags":["serialization","blob","cache","double-dispose"],"backgroundTag":"internal-invariant-violation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}