stride3d/stride · error · InvalidOperationException
Read-only backend.
Error message
Read-only backend.
What it means
FileOdbBackend.CreateStream writes new objects to the database by creating a temp file and moving it into the store on dispose. When the backend was opened read-only (IsReadOnly), any write attempt throws InvalidOperationException 'Read-only backend.'.
Solutions
- Open the object database backend writable (IsReadOnly = false) wherever writes are needed, e.g. in editor/user-data storage.
- Route writes to a separate writable backend (e.g. a user-profile store) instead of the read-only game-data backend.
- Check IsReadOnly before calling CreateStream and disable or redirect the write path in shipped builds.
Example fix
// before
using var writer = readOnlyBackend.CreateStream(); // throws
// after
if (backend.IsReadOnly)
backend = new FileOdbBackend(writableProvider, writablePath, isReadOnly: false);
using var writer = backend.CreateStream(); Defensive patterns
Strategy: validation
Validate before calling
if (backend.IsReadOnly)
throw new InvalidOperationException("Cannot create streams on a read-only object database backend.");
using var writer = backend.CreateStream(); Try / catch
try
{
using var writer = backend.CreateStream();
// write object...
}
catch (InvalidOperationException ex) when (ex.Message == "Read-only backend.")
{
backend = OpenWritableBackend(); // switch to a writable store
} Prevention
- Mount a writable backend (user data) for any runtime writes; keep shipped data read-only.
- Check IsReadOnly before entering write paths and disable write features accordingly.
- Never attempt writes to installed/read-only media; store user content in a profile directory.
When it happens
Trigger: Calling CreateStream on a FileOdbBackend constructed/mounted with IsReadOnly set — e.g. shipped game data opened read-only, then code (or an asset import path) tries to store new objects.
Common situations: Running a game built with read-only data mounts while an in-game importer/editor feature attempts to save; writing to installed Program Files or read-only media; forgetting to open a writable backend in tooling.
Related errors
- Can't pack files.
- Read-only object database.
- Could not find asset
- Expect name1=value1;name2=value2 format.
- assetItem must be an absolute path
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/dacff729d03c91a2.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.Serialization/Storage/FileOdbBackend.cs:132
if (!forceWrite && virtualFileProvider.FileExists(url))
return objectId;
using (var file = virtualFileProvider.OpenStream(tmpFileName, VirtualFileMode.Create, VirtualFileAccess.Write))
{
dataStream.CopyTo(file);
}
MoveToDatabase(tmpFileName, objectId, forceWrite);
return objectId;
}
/// <inheritdoc/>
public OdbStreamWriter CreateStream()
{
if (IsReadOnly)
throw new InvalidOperationException("Read-only backend.");
string tmpFileName = vfsTempUrl + Guid.NewGuid() + ".tmp";
Stream stream = virtualFileProvider.OpenStream(tmpFileName, VirtualFileMode.Create, VirtualFileAccess.Write);
return new DigestStream(stream, tmpFileName) { Disposed = x => MoveToDatabase(x.TemporaryName, x.CurrentHash) };
}
private void MoveToDatabase(string temporaryFilePath, ObjectId objId, bool forceWrite = false)
{
string fileUrl = BuildUrl(vfsRootUrl, objId);
lock (LockOnMove)
{
var fileExists = virtualFileProvider.FileExists(fileUrl);
// File may already exists, in this case we decide to not override it.
if (!fileExists || forceWrite)
{
tryView on GitHub (pinned to 96fad776d2)