stride3d/stride · error · InvalidOperationException

Bundle has not been loaded.

Error message

Bundle has not been loaded.

What it means

UnloadBundleRecursive searches loadedBundles for the bundle description being unloaded and throws InvalidOperationException when none matches (loadedBundleIndex == -1). The code attempted to unload a bundle that was never loaded (or already fully unloaded).

Solutions

  1. Track loaded bundles and only call UnloadBundle for bundles previously loaded successfully.
  2. Make unload logic idempotent: skip if the bundle is not currently loaded instead of assuming it is.
  3. Fix double-dispose (guard Dispose with a flag) so unload doesn't run twice.

Example fix

// before
backend.UnloadBundle("scene.bundle"); // may throw if never loaded
// after
if (backend.IsBundleLoaded("scene.bundle"))
    backend.UnloadBundle("scene.bundle");
Defensive patterns

Strategy: validation

Validate before calling

if (!backend.IsBundleLoaded(bundleUrl))
    return; // nothing to unload
backend.UnloadBundle(bundleUrl);

Try / catch

try
{
    backend.UnloadBundle(bundleUrl);
}
catch (InvalidOperationException ex) when (ex.Message == "Bundle has not been loaded.")
{
    // already unloaded; treat as no-op
}

Prevention

When it happens

Trigger: Calling UnloadBundle for a bundle name/url never passed to LoadBundle, unloading the same bundle twice, or recursive unloading reaching a dependency already unloaded.

Common situations: Shutdown/dispose logic running twice (double Dispose), error-recovery paths that unload bundles after a failed load, or unloading a bundle whose parent already recursively removed it.

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/9aaa37e53bddd37c. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.Serialization/Storage/BundleOdbBackend.cs:299

    {
        if (bundleName == null) throw new ArgumentNullException(nameof(bundleName));

        lock (loadedBundles)
        {
            int loadedBundleIndex = -1;

            for (int index = 0; index < loadedBundles.Count; index++)
            {
                var currentBundle = loadedBundles[index];
                if (currentBundle.BundleName == bundleName)
                {
                    loadedBundleIndex = index;
                    break;
                }
            }

            if (loadedBundleIndex == -1)
                throw new InvalidOperationException("Bundle has not been loaded.");

            var loadedBundle = loadedBundles[loadedBundleIndex];
            var bundle = loadedBundle.Description;
            if (--loadedBundle.ReferenceCount == 0)
            {
                // Remove and dispose stream from pool
                lock (loadedBundle.Streams)
                {
                    for (int index = 0; index < loadedBundle.Streams.Count; index++)
                    {
                        var stream = loadedBundle.Streams[index];
                        stream?.Dispose();
                        loadedBundle.Streams[index] = null;
                    }
                }

                // Actually unload bundle
                loadedBundles.RemoveAt(loadedBundleIndex);

View on GitHub (pinned to 96fad776d2)