stride3d/stride · error · InvalidOperationException

Destroying a blob not created through…

Error message

Destroying a blob not created through ObjectDatabase.CreateBlob.

What it means

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.

Solutions

  1. Only destroy blobs obtained from ObjectDatabase.CreateBlob on the same ObjectDatabase instance.
  2. Ensure destruction is idempotent — check/dispose once (guard with a flag or null out references).
  3. Use Blob.Dispose rather than DestroyBlob for blobs you did not create via CreateBlob.

Example fix

// before
var blob = new Blob(objectId, data); // manually created
ObjectDatabase.DestroyBlob(blob);
// after
var blob = ObjectDatabase.CreateBlob(stream); // created through the API
ObjectDatabase.DestroyBlob(blob);
Defensive patterns

Strategy: try-catch

Type guard

static bool CanDestroy(Blob blob) =>
    ObjectDatabase.LoadedBlobs != null && blob != null && ObjectDatabase.LoadedBlobs.ContainsKey(blob.ObjectId);

Try / catch

try { ObjectDatabase.DestroyBlob(blob); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Destroying a blob not created")) {
    // blob was foreign or already destroyed; fall back to Dispose
    blob?.Dispose();
}

Prevention

When it happens

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

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

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sources/core/Stride.Core.Serialization/Storage/ObjectDatabase.cs:474

                // Dispose the previously opened stream.
                stream.Dispose();
            }

            // Lookup adds a reference
            blob.AddReference();
        }

        return blob;
    }

    internal static void DestroyBlob(Blob blob)
    {
        // Remove blob from cache when destroyed
        lock (LoadedBlobs)
        {
            if (!LoadedBlobs.Remove(blob.ObjectId))
                throw new InvalidOperationException("Destroying a blob not created through ObjectDatabase.CreateBlob.");
        }
    }

    private Stream OpenStreamForRead(ObjectId objectId, VirtualFileMode mode, VirtualFileAccess access, VirtualFileShare share)
    {
        if (BundleBackend?.Exists(objectId) == true)
            return BundleBackend.OpenStream(objectId, mode, access, share);

        if (backendRead1.Exists(objectId))
            return backendRead1.OpenStream(objectId, mode, access, share);

        if (backendRead2?.Exists(objectId) == true)
            return backendRead2.OpenStream(objectId, mode, access, share);

        throw new FileNotFoundException();
    }
}

View on GitHub (pinned to 96fad776d2)