stride3d/stride · error · InvalidOperationException
Bundle is being loaded twice (either cyclic dependency or…
Error message
Bundle {0} is being loaded twice (either cyclic dependency or concurrency issue) What it means
BundleOdbBackend.ResolveBundle tracks in-progress bundle resolutions in resolvedBundles, storing null as an 'in progress' marker. If a resolution request arrives for a bundle already marked in progress, it means a dependency cycle or re-entrant/concurrent load of the same bundle, and Stride throws InvalidOperationException.
Solutions
- Inspect bundle dependency lists and remove the cycle (bundle A -> B -> A).
- Serialize bundle loading: ensure vfsUrl / LoadBundle calls are made from one thread or guarded by an external lock during startup.
- Check for duplicate mounts of the same bundle under the same name and deduplicate.
Example fix
// before (two threads)
Task.Run(() => backend.LoadBundle("assets.bundle", ...));
Task.Run(() => backend.LoadBundle("assets.bundle", ...));
// after
lock (loadLock) { backend.LoadBundle("assets.bundle", ...); } // or await once, share result Defensive patterns
Strategy: try-catch
Validate before calling
if (IsBundleResolutionInProgress(bundleName))
throw new InvalidOperationException($"Cycle detected while resolving {bundleName}"); Try / catch
try
{
return backend.vfsUrl(bundleName, throwExceptionIfNotFound: true);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("being loaded twice"))
{
// break the cycle or serialize access, then retry
breakDependencyCycle(bundleName);
return null;
} Prevention
- Audit bundle dependency graphs for cycles before shipping.
- Load bundles from a single thread or with an application-level lock during startup.
- Avoid mounting the same bundle under duplicate names.
When it happens
Trigger: vfsUrl is called for a bundle whose resolution has already started but not finished — i.e. bundle A depends on B and B (directly or transitively) depends on A, or two threads simultaneously request vfsUrl for the same bundle name outside the lock.
Common situations: Cyclic .bundle dependencies authored in a build script; multiple threads/clients hitting the same virtual file system URL concurrently during startup.
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
- Aynchronous lock can only be acquired from a micro-thread…
- Trying to enter a lock that has already been entered
- Trying to lock while another thread owns the lock.
- This operation is already in progress
- Unable to create a transaction. A rollback or rollforward…
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/876adcf4479a623b.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.Serialization/Storage/BundleOdbBackend.cs:94
private Task<string?> DefaultBundleResolve(string bundleName)
{
// Try to find [bundleName].bundle
var bundleFile = VirtualFileSystem.Combine(BundleDirectory, bundleName + BundleExtension);
if (VirtualFileSystem.FileExists(bundleFile))
return Task.FromResult<string?>(bundleFile);
return Task.FromResult<string?>(null);
}
private async Task<string> ResolveBundle(string bundleName, bool throwExceptionIfNotFound)
{
string? bundleUrl;
lock (resolvedBundles)
{
if (resolvedBundles.TryGetValue(bundleName, out bundleUrl))
{
if (bundleUrl == null)
throw new InvalidOperationException(string.Format("Bundle {0} is being loaded twice (either cyclic dependency or concurrency issue)", bundleName));
return bundleUrl;
}
// Store null until resolved (to detect cyclic dependencies)
resolvedBundles[bundleName] = null;
}
if (BundleResolve != null)
{
// Iterate over each handler and find the first one that returns non-null result
foreach (var bundleResolvedHandler in BundleResolve.GetInvocationList().Cast<BundleResolveDelegate>())
{
// Use handler to resolve package
bundleUrl = await bundleResolvedHandler(bundleName);
if (bundleUrl != null)
break;
}
}View on GitHub (pinned to 96fad776d2)