Skip to content
>GLB_
Go back

DuckDB concurrency in 2026: why you can't run dbt and DBeaver at the same time

If you work with DuckDB locally, you’ll hit this eventually:

IO Error: Could not set lock on file "tuva.duckdb": Resource temporarily unavailable

This happens when two processes try to write to the same DuckDB database file simultaneously. In a typical dbt development workflow, DBeaver is the silent culprit.

DuckDB’s concurrency model

DuckDB is designed for single-process use. It implements MVCC (multi-version concurrency control) for reads, so multiple read connections can coexist, but it enforces a single-writer constraint at the file level using OS file locks.

When you open a .duckdb file in DBeaver, the client opens a read-write connection by default. That connection holds a write lock on the file. The lock persists as long as the DBeaver connection is active — which is usually as long as DBeaver is running.

When dbt tries to run against the same file, it also needs a write connection. The OS lock is already held by DBeaver, so dbt gets EWOULDBLOCK and surfaces it as the IO error above.

Why this is specific to DuckDB

Most databases separate the server process from client connections. PostgreSQL, MySQL, BigQuery — the client connects to a process that manages concurrent access. DuckDB in file mode has no server process. Every connection goes directly to the file, and the file lock is the only coordination mechanism.

DuckDB does offer a client-server mode (duckdb --listen or MotherDuck), but for local development most setups use the file directly.

The fix: set DBeaver to read-only mode

DBeaver supports DuckDB connection properties. To prevent DBeaver from acquiring a write lock, set read_only = true in the connection configuration.

In DBeaver:

  1. Open the connection editor for your DuckDB connection
  2. Go to the “Driver Properties” or “Connection Properties” tab
  3. Add the property read_only with value true
  4. Reconnect

DBeaver will reconnect in read-only mode. You can still run SELECT queries, browse tables, and inspect schemas. INSERT, UPDATE, CREATE, and DROP will fail — but for a development analysis tool, that’s almost always fine.

What read-only mode actually does

Under the hood, DuckDB opens the file with O_RDONLY and skips the file lock acquisition. Multiple read-only connections can coexist with each other indefinitely. A single write connection can coexist with any number of read-only connections, with MVCC snapshot isolation guarantees for the readers.

The safe state for development:

The same conflict with Streamlit

DBeaver isn’t the only tool that causes this. The same lock conflict appears with:

For a Streamlit app that reads from the same database dbt writes to, open DuckDB explicitly in read-only mode:

import duckdb

con = duckdb.connect("tuva.duckdb", read_only=True)

This is the right pattern for any analytics application that reads from a DuckDB file that another process might be writing to.

What about in-memory DuckDB?

In-memory DuckDB (duckdb.connect() with no file path) doesn’t have this problem — each connection gets its own isolated in-memory database. But in-memory databases don’t persist between process restarts, which makes them unsuitable for the dbt-to-Streamlit workflow where you build once with dbt and read many times with Streamlit.

The future

DuckDB 1.10 improved error messaging for lock conflicts but didn’t change the underlying single-writer model. The constraint is fundamental to how DuckDB achieves its performance characteristics — maintaining a separate server process would add overhead that defeats the point of an embedded analytics database. For production multi-writer scenarios, MotherDuck handles this differently. For local development, read-only mode for your analysis tools is the practical answer.


Share this post:

Related Posts


Previous Post
Deploying a Streamlit analytics app in one afternoon
Next Post
Feature engineering from claims data for a Random Forest classifier