stride3d/stride · error · InvalidOperationException
Can't pack files.
Error message
Can't pack files.
What it means
ObjectDatabase.CreateBundle throws this when the bundle backend (IOdbBackend for writing bundles) is null. Bundles are prepacked asset files; without a valid BundleOdbBackend there is nowhere to write the pack, so the library refuses rather than fail later. Callers typically get here via topBundleUrl when preparing asset bundles.
Solutions
- Configure the ObjectDatabase with a valid BundleOdbBackend (BundleDirectory set) before calling CreateBundle.
- Run bundle creation from the game studio/asset compiler where a writable bundle backend is attached, not at runtime from a read-only database.
- Guard the call site: skip or log when bundleBackend is null instead of invoking CreateBundle.
Example fix
// before
var url = database.CreateBundle(objectIds, bundleName, null, disableCompressionIds, indexMap, dependencies, false);
// after
var backend = new BundleOdbBackend(database, bundleDirectory, VirtualFileSystem.GetFileProvider(bundleDirectory));
if (backend == null) throw new InvalidOperationException("Bundle backend not configured");
var url = database.CreateBundle(objectIds, bundleName, backend, disableCompressionIds, indexMap, dependencies, false); Defensive patterns
Strategy: validation
Validate before calling
if (bundleBackend == null)
throw new InvalidOperationException("CreateBundle requires a configured BundleOdbBackend; initialize the object database with a writable bundle backend first."); Try / catch
try { url = database.CreateBundle(ids, name, backend, ...); }
catch (InvalidOperationException ex) when (ex.Message == "Can't pack files.") {
logger.Error("Bundle backend not configured; cannot pack assets.");
} Prevention
- Always initialize ObjectDatabase with a BundleOdbBackend before any pack operation
- Only run bundle creation in tooling contexts with write access
- Add an assertion/precondition at API boundaries that accept backends
When it happens
Trigger: Calling ObjectDatabase.CreateBundle (directly or via topBundleUrl) when the object database was opened without a bundle-capable write backend (bundleBackend parameter null).
Common situations: Opening a read-only object database (e.g. mounted from an APK or read-only asset store) and then attempting to generate bundles; forgetting to configure BundleOdbBackend in the game asset compiler/packager; passing a null backend programmatically.
Related errors
- SetAssetObject has already been called with a different…
- Bundle has not been loaded.
- Read-only backend.
- Read-only object database.
- Unable to process the given log message.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/a8154c4f104496f8.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.Serialization/Storage/ObjectDatabase.cs:144
}
public void Dispose()
{
backendRead1.Dispose();
if (backendRead2 != null && !ReferenceEquals(backendRead2, backendRead1))
{
backendRead2.Dispose();
}
if (backendWrite != null && !ReferenceEquals(backendRead2, backendWrite) && !ReferenceEquals(backendRead2, backendRead1))
{
backendWrite.Dispose();
}
}
public string? CreateBundle(ObjectId[] objectIds, string bundleName, BundleOdbBackend bundleBackend, ISet<ObjectId> disableCompressionIds, Dictionary<string, ObjectId> indexMap, IList<string> dependencies, bool useIncrementalBundle)
{
if (bundleBackend == null)
throw new InvalidOperationException("Can't pack files.");
if (objectIds.Length == 0)
return null;
var packUrl = bundleBackend.BundleDirectory + bundleName + BundleOdbBackend.BundleExtension; // we don't want the pack to be compressed in the APK on android
// Create pack
BundleOdbBackend.CreateBundle(packUrl, backendRead1, objectIds, disableCompressionIds, indexMap, dependencies, useIncrementalBundle);
return packUrl;
}
public bool TryGetObjectLocation(ObjectId objectId, [NotNullWhen(true)] out string? filePath, out long start, out long end)
{
if (BundleBackend != null && BundleBackend.TryGetObjectLocation(objectId, out filePath, out start, out end))
return true;
foreach (var backend in new[] { backendRead1, backendRead2 })
{View on GitHub (pinned to 96fad776d2)