unable to open database file doesn’t say which file SQLite tried or why it failed. The list of causes is short, though, and in this article I’ll walk you through each one with the exact error it produces and the fix.
TLDR
- The error is SQLite’s
SQLITE_CANTOPEN(error 14): it couldn’t open or create a file it needed. - Check first that the folder in the path exists. SQLite creates a missing file, not a missing folder.
- A relative path is resolved from the working directory, not from your script. Build an absolute path.
- Python doesn’t expand
~, and a Windows path in a normal string turns\nand\tinto control characters. - Check permissions: the file needs read access, and its folder needs write access for new files and journals.
file is not a databaseanddatabase is lockedare different errors with different fixes.
What is “unable to open database file”?
It’s the message for SQLite’s SQLITE_CANTOPEN result code, which “indicates that SQLite was unable to open a file. The file in question might be a primary database file or one of several temporary disk files” (SQLite result codes). Each library wraps the same message in its own exception:
| Where | What you see |
|---|---|
| sqlite3 shell | Error: unable to open database "data/app.db": unable to open database file |
| Python | sqlite3.OperationalError: unable to open database file |
| SQLAlchemy | (sqlite3.OperationalError) unable to open database file |
| .NET (Microsoft.Data.Sqlite) | SQLite Error 14: 'unable to open database file' |
The shell and Python rows come from the test run below. The SQLAlchemy and .NET rows show how those libraries report it (not executed here). The shell’s message names the path it tried:
$ sqlite3 data/app.db "SELECT 1;"
Error: unable to open database "data/app.db": unable to open database file
How to fix “unable to open database file”
To show you each cause, I will run one small Python script that opens a database in seven broken ways, as a normal (non-root) user. Python 3.11 and later add the SQLite error name to the exception, which tells the causes apart:
import os
import sqlite3
def try_open(label, path, uri=False, sql="CREATE TABLE IF NOT EXISTS t (x)"):
try:
con = sqlite3.connect(path, uri=uri)
con.execute(sql)
con.commit()
con.close()
print(f"{label}: OK")
except sqlite3.OperationalError as e:
print(f"{label}: OperationalError: {e} [{e.sqlite_errorname}]")
try_open("missing folder", "data/app.db")
try_open("tilde path", "~/app.db")
try_open("path is a folder", "reports")
try_open("mode=rw, file missing", "file:new.db?mode=rw", uri=True)
try_open("no read permission", "locked.db")
try_open("new file in read-only folder", "readonly_dir/new.db")
try_open("write in read-only folder", "readonly_dir/app.db", sql="INSERT INTO t VALUES (1)")
os.makedirs("data", exist_ok=True)
try_open("missing folder, after makedirs", "data/app.db")
print("expanduser:", os.path.expanduser("~/app.db").startswith("/"))
$ python3.14 causes.py
missing folder: OperationalError: unable to open database file [SQLITE_CANTOPEN]
tilde path: OperationalError: unable to open database file [SQLITE_CANTOPEN]
path is a folder: OperationalError: unable to open database file [SQLITE_CANTOPEN]
mode=rw, file missing: OperationalError: unable to open database file [SQLITE_CANTOPEN]
no read permission: OperationalError: unable to open database file [SQLITE_CANTOPEN]
new file in read-only folder: OperationalError: unable to open database file [SQLITE_CANTOPEN]
write in read-only folder: OperationalError: attempt to write a readonly database [SQLITE_READONLY_DIRECTORY]
missing folder, after makedirs: OK
expanduser: True
Each fix below matches one line of that output.
Step 1: Create the folder, or fix the path
SQLite creates a missing database file, but not a missing folder. data/app.db fails until data/ exists, and the last lines of the output show it working after os.makedirs("data", exist_ok=True). Check the path for typos too, such as .sqllite3 instead of .sqlite3.
Step 2: Use an absolute path, not one relative to where you ran the script
A relative path like data/app.db is looked up from the current working directory. Run the same script from another folder and it fails. A cron job or a service can start your script from a different folder too:
import os
import sqlite3
from pathlib import Path
print("working directory:", os.getcwd())
try:
sqlite3.connect("data/app.db").execute("SELECT 1")
print("relative path: OK")
except sqlite3.OperationalError as e:
print("relative path:", e)
# Build the path from this file's location instead of the working directory
db_path = Path(__file__).resolve().parent / "data" / "app.db"
sqlite3.connect(db_path).execute("SELECT 1")
print("path next to the script: OK")
$ cd /tmp && python3.14 /tmp/sqldocs-f1-unable-to-open/app/cwd_demo.py
working directory: /private/tmp
relative path: unable to open database file
path next to the script: OK
Building the path from __file__ makes it independent of where the script was started.
Step 3: Expand ~ yourself
Python passes ~/app.db to SQLite as written, and SQLite looks for a folder literally named ~. That’s the “tilde path” line in the output. Wrap the path in os.path.expanduser() or use Path.home().
Step 4: Fix Windows paths in Python strings
In a normal Python string, \n is a newline and \t is a tab, so "C:\new\temp.db" isn’t the path you typed:
# Backslashes in a normal Python string are escape sequences
bad = "C:\new\temp.db" # \n is a newline and \t is a tab
good = r"C:\new\temp.db" # raw string: the backslashes stay
print("newline in bad path:", "\n" in bad, "| length:", len(bad))
print("newline in raw path:", "\n" in good, "| length:", len(good))
$ python3.14 windows_path.py
newline in bad path: True | length: 12
newline in raw path: False | length: 14
Use a raw string (r"C:\new\temp.db"), forward slashes ("C:/new/temp.db") or pathlib.Path.
Step 5: Point at a file, not a folder
If the path names an existing folder (the “path is a folder” line), SQLite can’t open it as a database. Add the file name.
Step 6: Fix file and folder permissions
SQLite needs read access to the database file, and write access to its folder for two jobs: creating a new database, and creating the journal file that every write uses. The run shows both failures, and one of them comes with a different message, attempt to write a readonly database (SQLITE_READONLY_DIRECTORY). SQLite’s docs explain that it appears when the process can’t “create a journal file in the same directory as the database” (SQLite result codes).
Check the permissions with ls -l:
$ ls -ld readonly_dir locked.db | awk '{print $1, $NF}'
---------- locked.db
dr-xr-xr-x readonly_dir
Then give the user that runs your code access. Here chmod is enough, because the test user owns the files. On a server, also check which user the app runs as and use chown if that user doesn’t own the file:
$ chmod 644 locked.db && chmod 755 readonly_dir
$ python3.14 -c "import sqlite3; c = sqlite3.connect('readonly_dir/app.db'); c.execute('INSERT INTO t VALUES (1)'); c.commit(); print('write OK')"
write OK
$ sqlite3 locked.db "SELECT count(*) FROM t;"
0
On a web server or in Docker, the app can run as a different user from the one you log in as. Check permissions as that user.
Step 7: Only open an existing file on purpose
The URI parameter mode=rw opens the database read-write but never creates it (SQLite URI filenames). That’s why “mode=rw, file missing” fails. It’s useful when a wrong path should fail instead of quietly creating an empty database, so check the path rather than removing mode=rw.
Errors that look similar but aren’t
A corrupted or non-SQLite file gives a different error, file is not a database (SQLITE_NOTADB, error 26), not “unable to open”:
$ echo 'this is not a database' > notes.db
$ sqlite3 notes.db "SELECT 1;"
Error: in prepare, file is not a database (26)
$ python3.14 -c "import sqlite3; sqlite3.connect('notes.db').execute('SELECT 1')" 2>&1 | tail -1
sqlite3.DatabaseError: file is not a database
| Error | Meaning | Guide |
|---|---|---|
unable to open database file | SQLite couldn’t open or create a file | this page |
file is not a database | The file opened, but it isn’t an SQLite database or its header is damaged | Database disk image is malformed |
database is locked | Another connection holds a lock | Database is locked |
attempt to write a readonly database | No write access to the file or its folder | Step 6 above |
no such table | The file opened, but it’s a different database, a new empty one if the path was wrong | Check the path (Steps 1 and 2) |
The last row is a side effect of Step 1’s behaviour. When the folder exists but the file name is wrong, SQLite creates a new, empty database. The first query then fails with no such table.
Conclusion
“Unable to open database file” is a file-system problem, not a database problem. Check the folder exists, then build an absolute path, then check permissions as the user your code runs as. If the message is file is not a database or database is locked instead, you’re dealing with a different error.
Further reading
- Database is locked
- Database disk image is malformed
- SQLite locks
- Python SQLite3 tutorial
- SQLite commands
- The SQLite handbook
- All SQLite guides by topic
- SQLite docs: Result and error codes, URI filenames
Tested with SQLite 3.51.0 (sqlite3 shell) and Python 3.14.6 with SQLite 3.53.4 on macOS, 7 October 2026.
