tursodatabase/turso · error · SqliteException
14
14
Error message
SQLite Error {errorCode}: 'unable to open database file'. What it means
Thrown by NormalizeDataSource when Mode is ReadOnly or ReadWrite and the resolved database file does not exist, with SQLite code 14 (SQLITE_CANTOPEN). The check happens before open: these two modes never create files, so a missing file is reported as 'unable to open database file'. The path is resolved relative to AppContext.BaseDirectory when not rooted, not relative to the process working directory.
Solutions
- Use Mode=ReadWriteCreate (the default) if the database is allowed to be created on first open.
- Verify the resolved path exists before connecting: Path.Combine(AppContext.BaseDirectory, relativePath) then File.Exists — remember relative paths resolve against the app base directory, not Environment.CurrentDirectory.
- For read-only access to an optional file, deploy or copy the .db file to the publish output (mark it CopyToOutputDirectory in the project file) or use an absolute path.
Example fix
// before
var conn = new SqliteConnection("Data Source=data/app.db;Mode=ReadOnly");
conn.Open(); // throws if app.db missing
// after
var dbPath = Path.Combine(AppContext.BaseDirectory, "data", "app.db");
if (!File.Exists(dbPath))
throw new FileNotFoundException("Database not deployed", dbPath);
var conn = new SqliteConnection($"Data Source={dbPath};Mode=ReadOnly"); Defensive patterns
Strategy: validation
Validate before calling
var path = Path.IsPathRooted(ds) ? ds : Path.Combine(AppContext.BaseDirectory, ds);
if ((mode is SqliteOpenMode.ReadOnly or SqliteOpenMode.ReadWrite) && !File.Exists(path))
throw new FileNotFoundException("Database file missing (required by Mode).", path); Try / catch
try { conn.Open(); }
catch (SqliteException ex) when (ex.SqliteErrorCode == 14)
{ /* log resolved path, verify deployment, or fall back to ReadWriteCreate */ } Prevention
- Remember relative data sources resolve against AppContext.BaseDirectory, not the current working directory.
- Mark required .db files CopyToOutputDirectory in the project, or create them with ReadWriteCreate on first run.
When it happens
Trigger: Opening 'Data Source=app.db;Mode=ReadOnly' (or Mode=ReadWrite) when app.db is absent at the resolved path; using a relative filename and running from a different working directory, because the binding combines it with AppContext.BaseDirectory; '|DataDirectory|' substitution resolving to a folder without the file.
Common situations: Assuming the file will be created like the default ReadWriteCreate mode does; deploying an app where the .db file was not copied to the output/publish folder; relative-path bugs in test runners (dotnet test runs from bin/Debug) and single-file publishes; wrong casing of the filename on Linux.
Related errors
- 1
- Auth Token requires a remote Turso URL Data Source.
- Cannot convert to .
- Column name is ambiguous between and .
- Column was not found.
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20).
Data as JSON: /api/errors/998b6db6d8a45d1c.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteConnection.cs:1096
? GetSharedMemoryFile(dataSource)
: ":memory:";
if (dataSource.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
return NormalizeUriDataSource(dataSource);
const string dataDirectory = "|DataDirectory|";
if (dataSource.StartsWith(dataDirectory, StringComparison.OrdinalIgnoreCase))
{
var baseDirectory = AppDomain.CurrentDomain.GetData("DataDirectory") as string
?? AppContext.BaseDirectory;
dataSource = Path.Combine(baseDirectory, dataSource[dataDirectory.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
}
var filename = Path.IsPathRooted(dataSource)
? dataSource
: Path.Combine(AppContext.BaseDirectory, dataSource);
if ((options.Mode == SqliteOpenMode.ReadOnly || options.Mode == SqliteOpenMode.ReadWrite) && !File.Exists(filename))
throw new SqliteException(Properties.Resources.SqliteNativeError(SQLITE_CANTOPEN, "unable to open database file"), SQLITE_CANTOPEN);
return filename;
}
private static string NormalizeUriDataSource(string dataSource)
{
var queryStart = dataSource.IndexOf('?', StringComparison.Ordinal);
var path = queryStart < 0 ? dataSource[5..] : dataSource[5..queryStart];
var query = queryStart < 0 ? string.Empty : dataSource[(queryStart + 1)..];
foreach (var part in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
{
var pieces = part.Split('=', 2);
if (!pieces[0].Equals("mode", StringComparison.OrdinalIgnoreCase))
continue;
var mode = pieces.Length == 2 ? pieces[1] : string.Empty;
if (!mode.Equals("ro", StringComparison.OrdinalIgnoreCase)
&& !mode.Equals("rw", StringComparison.OrdinalIgnoreCase)View on GitHub (pinned to 6c72522679)