fix/duckdb/cannot-open-file
DuckDB error

Cannot open file

Updated Aug 27, 20265-min read
TL;DR

DuckDB could not read the file the query pointed at. Almost always a path problem, a lock held by another process, or a glob that matched nothing. A close relative is 'Error when sniffing file', which fires when the file opened fine but the CSV auto-detector could not figure out its shape.

  • ·The path is relative to a working directory you did not expect (notebook vs CLI vs cron)
  • ·Another process is holding the file open (Windows lock, a second DuckDB connection, DBeaver, Excel)
  • ·The glob pattern matched zero files and DuckDB is complaining about the literal glob string
  • ·You hit the OS open-file-descriptor limit while reading thousands of files at once
  • ·The file opened but the CSV sniffer failed, and you are staring at 'Invalid Input Error: Error when sniffing file' instead of an IO error
CHECK FIRSTPrint the absolute path from the exact process that runs DuckDB, then ls it from the same shell. Path drift between REPL, notebook, cron, and CLI causes most of these.

What you're seeing

DuckDB surfaces file access failures as 'IO Error: Cannot open file'. When the file opens but the CSV auto-detector cannot parse it, you get 'Invalid Input Error: Error when sniffing file' instead. Both show up when searching for 'cannot open file' because the underlying user complaint is the same: DuckDB will not read my file.

text
Error: IO Error: Cannot open file "data.csv": No such file or directory

# Windows, file locked by another process:
Error: IO Error: Cannot open file "C:\data\mydb.duckdb": The process cannot access the file because it is being used by another process.

# Python client:
duckdb.duckdb.IOException: IO Error: Cannot open file "events/*.parquet": No such file or directory

# Reading many files at once:
IO Error: Cannot open file "part-01734.json": Too many open files

# CSV sniffer variant (file opened, parser failed):
Invalid Input Error: Error when sniffing file "data.csv". It was not possible to automatically detect the CSV Parsing dialect/types
Also seen as: duckdb cannot open file, duckdb IO Error Cannot open file, duckdb IOException Cannot open file, duckdb No such file or directory, duckdb file being used by another process, duckdb error when sniffing file, duckdb Too many open files

What's causing this

Ranked most-likely first.

  1. 1

    The path is wrong or resolves against an unexpected working directory

    A relative path like 'data.csv' resolves against the process CWD, which is the notebook kernel dir in Jupyter, the launch dir in the CLI, and often '/' in cron or a systemd unit. The file exists, but not where DuckDB looked.

  2. 2

    Another process is holding the file open

    On Windows this is common: DBeaver, Excel, another Python REPL, or a prior DuckDB connection that was not closed still holds the .duckdb or .csv file. On Linux and macOS the same happens if two DuckDB connections open the same non-read-only database file simultaneously.

  3. 3

    A glob pattern matched zero files

    read_csv('events/*.parquet') or read_json('logs/2026-08-*.json') passes the literal glob to DuckDB. If the pattern expands to nothing, DuckDB reports 'Cannot open file' with the glob string quoted, which reads like a path error but is really an empty match.

  4. 4

    You hit the OS file descriptor limit

    Reading a directory of thousands of small JSON, CSV, or Parquet files at once can exceed the default ulimit -n of 1024 on Linux and macOS. DuckDB then reports 'Cannot open file ...: Too many open files'.

  5. 5

    A .duckdb.wal file is missing after a crash or concurrent attach

    If the writer crashed, or two processes raced on ATTACH/DETACH, the WAL sidecar can go missing while the main .duckdb file is present. DuckDB reports 'Cannot open file "foo.duckdb.wal": No such file or directory' on the next open.

  6. 6

    The CSV sniffer failed even though the file opened

    'Error when sniffing file' is a different failure mode: the IO layer read the file fine, but auto-detection could not decide on delimiter, quote, escape, or column types. Gzip'd tab-delimited files, semicolon-separated European CSVs, and files with embedded quotes are the common triggers.

How to fix it

Step 1: confirm the exact path DuckDB sees

Before touching anything, print the absolute path from the process that runs DuckDB and stat it from the same shell. Path drift is the single biggest cause of this error.

python
import os, duckdb
path = "data.csv"
print("cwd:", os.getcwd())
print("abs:", os.path.abspath(path))
print("exists:", os.path.exists(path))
duckdb.sql(f"SELECT * FROM read_csv('{os.path.abspath(path)}') LIMIT 1")

Step 2: make sure no other process holds the file

Close every other tool that might have the file open: DBeaver, Excel, a second Python REPL, a background notebook kernel. On the CLI, quit any open .duckdb shell. If you need concurrent readers, open the database in read-only mode.

python
# Python: open read-only so multiple processes can share the file
con = duckdb.connect("mydb.duckdb", read_only=True)

# CLI: --readonly flag
# duckdb --readonly mydb.duckdb

Step 3: verify a glob actually matches something

If the path contains a wildcard, expand it with your shell or a language glob before handing it to DuckDB. An empty match becomes a 'Cannot open file' with the raw glob quoted.

bash
# Shell: does the pattern expand?
ls events/*.parquet

# Python:
import glob
files = glob.glob("events/*.parquet")
print(len(files), "files")

Step 4: raise the OS file descriptor limit or batch the read

For 'Too many open files' when reading a folder of thousands of files, raise ulimit -n or read the files in batches. On macOS the default of 256 or 1024 is easy to blow past.

bash
# Check the current limit
ulimit -n

# Raise it for this shell (Linux and macOS)
ulimit -n 65536

# Or batch the read from the client side
# for chunk in chunks(files, 500):
#     con.sql(f"SELECT ... FROM read_json({chunk})")

Step 5: if a .duckdb.wal is missing, use a fresh copy of the database

A missing WAL after a crash means the write-ahead log DuckDB expected is gone. Do not just delete the main .duckdb file: restore from a known-good backup, or if the WAL was empty, open the database fresh from a copy taken before the incident.

bash
# Move the incomplete pair aside first, do not delete
mv mydb.duckdb mydb.duckdb.broken
mv mydb.duckdb.wal mydb.duckdb.wal.broken 2>/dev/null

# Restore from backup
cp backups/mydb-2026-08-27.duckdb mydb.duckdb

Step 6: for 'Error when sniffing file', pass parser options manually

When the sniffer gives up, tell DuckDB the shape of the file directly. delim, quote, escape, header, and columns cover most cases. For badly formed CSVs, turn off strict_mode so bad rows do not abort the read.

sql
-- European semicolon CSV, no header
SELECT * FROM read_csv(
  'prices.csv',
  delim=';',
  header=false,
  columns={'date':'DATE','symbol':'VARCHAR','price':'DOUBLE'}
);

-- Gzip TSV that the sniffer chokes on
SELECT * FROM read_csv(
  'events.tsv.gz',
  delim='\t',
  header=true,
  compression='gzip'
);

-- Skip malformed rows instead of aborting
SELECT * FROM read_csv(
  'messy.csv',
  strict_mode=false,
  ignore_errors=true
);

Step 7: as a last resort, run the sniffer standalone to see what it guessed

sniff_csv returns the parameters DuckDB would use, so you can inspect the guess and hand-tune it. Useful when the sniffer succeeds on a sample but fails on the full file.

sql
SELECT * FROM sniff_csv('data.csv');
-- Then feed the returned Delimiter, Quote, Escape, Header, Columns
-- into an explicit read_csv() call.

Prevention

Pass absolute paths to DuckDB in any code that runs outside an interactive shell: notebooks, cron jobs, systemd units, containers. Wrap relative paths with os.path.abspath or pathlib.Path().resolve() at the boundary so a change in working directory never silently breaks a load.

Open shared .duckdb files in read-only mode from any process that only needs to query them. Reserve write access to a single writer, and close connections in a finally block so a crash does not leave a lock behind.

For pipelines that read many small files, either compact them into fewer larger Parquet files or set ulimit -n high enough to cover the worst case. A pipeline that works on 500 files today will fail on 5000 tomorrow if the descriptor limit is not raised.

When you know the CSV shape, do not rely on the sniffer. Pin delim, quote, header, and columns explicitly. The sniffer is a convenience for exploration, not a contract for production loads.

Debug DuckDB faster

1bench is a native GUI for DuckDB. Inspect queries, connections, and settings without leaving the app. See what's happening before you have to Google it.

Open DuckDB in 1bench