SQLite “Unable to Open Database File”: Causes and Fixes (Tested)

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 \n and \t into control characters.
  • Check permissions: the file needs read access, and its folder needs write access for new files and journals.
  • file is not a database and database is locked are 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:

WhereWhat you see
sqlite3 shellError: unable to open database "data/app.db": unable to open database file
Pythonsqlite3.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
ErrorMeaningGuide
unable to open database fileSQLite couldn’t open or create a filethis page
file is not a databaseThe file opened, but it isn’t an SQLite database or its header is damagedDatabase disk image is malformed
database is lockedAnother connection holds a lockDatabase is locked
attempt to write a readonly databaseNo write access to the file or its folderStep 6 above
no such tableThe file opened, but it’s a different database, a new empty one if the path was wrongCheck 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

Tested with SQLite 3.51.0 (sqlite3 shell) and Python 3.14.6 with SQLite 3.53.4 on macOS, 7 October 2026.