{"record":{"id":"876adcf4479a623b","repo":"stride3d/stride","slug":"bundle-0-is-being-loaded-twice-either-cyclic-dependency-or","errorCode":null,"errorMessage":"Bundle {0} is being loaded twice (either cyclic dependency or concurrency issue)","messagePattern":"Bundle (.+?) is being loaded twice \\(either cyclic dependency or concurrency issue\\)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.Serialization/Storage/BundleOdbBackend.cs","lineNumber":94,"sourceCode":"    private Task<string?> DefaultBundleResolve(string bundleName)\n    {\n        // Try to find [bundleName].bundle\n        var bundleFile = VirtualFileSystem.Combine(BundleDirectory, bundleName + BundleExtension);\n        if (VirtualFileSystem.FileExists(bundleFile))\n            return Task.FromResult<string?>(bundleFile);\n\n        return Task.FromResult<string?>(null);\n    }\n\n    private async Task<string> ResolveBundle(string bundleName, bool throwExceptionIfNotFound)\n    {\n        string? bundleUrl;\n        lock (resolvedBundles)\n        {\n            if (resolvedBundles.TryGetValue(bundleName, out bundleUrl))\n            {\n                if (bundleUrl == null)\n                    throw new InvalidOperationException(string.Format(\"Bundle {0} is being loaded twice (either cyclic dependency or concurrency issue)\", bundleName));\n                return bundleUrl;\n            }\n\n            // Store null until resolved (to detect cyclic dependencies)\n            resolvedBundles[bundleName] = null;\n        }\n\n        if (BundleResolve != null)\n        {\n            // Iterate over each handler and find the first one that returns non-null result\n            foreach (var bundleResolvedHandler in BundleResolve.GetInvocationList().Cast<BundleResolveDelegate>())\n            {\n                // Use handler to resolve package\n                bundleUrl = await bundleResolvedHandler(bundleName);\n                if (bundleUrl != null)\n                    break;\n            }\n        }","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Serialization/Storage/BundleOdbBackend.cs#L76-L112","documentation":"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.","triggerScenarios":"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.","commonSituations":"Cyclic .bundle dependencies authored in a build script; multiple threads/clients hitting the same virtual file system URL concurrently during startup.","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."],"exampleFix":"// before (two threads)\nTask.Run(() => backend.LoadBundle(\"assets.bundle\", ...));\nTask.Run(() => backend.LoadBundle(\"assets.bundle\", ...));\n// after\nlock (loadLock) { backend.LoadBundle(\"assets.bundle\", ...); } // or await once, share result","handlingStrategy":"try-catch","validationCode":"if (IsBundleResolutionInProgress(bundleName))\n    throw new InvalidOperationException($\"Cycle detected while resolving {bundleName}\");","typeGuard":null,"tryCatchPattern":"try\n{\n    return backend.vfsUrl(bundleName, throwExceptionIfNotFound: true);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"being loaded twice\"))\n{\n    // break the cycle or serialize access, then retry\n    breakDependencyCycle(bundleName);\n    return null;\n}","preventionTips":["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."],"tags":["bundles","concurrency","cyclic-dependency"],"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-15T23:17:13.987Z"}