Recent development · Practical technical guide · 22 October 2026
What DuckDB 2.0.0 LTS changes for teams that run it in production: database files that older versions cannot open, the lambda syntax and integer division changes, the new parser, triggers and their limits, the Quack client-server protocol, and an upgrade order for fleets with many DuckDB processes.
The release in one paragraph
DuckDB 2.0.0, released on 21 October 2026, is a long-term support release supported until at least October 2027. The announcement calls it a feature release: a polished Quack client-server protocol with a new CONNECT statement, asynchronous I/O for Parquet, CSV and native storage, a first-class VARIANT type, triggers, a PEG parser as the default, an agent mode in the CLI, self-hosted extension repositories and the removal of the ICU library dependency. DuckDB 1.4 LTS reaches end of life on 17 November 2026, and 1.5 is supported until March 2027 to give users time to test. The CLI and Python client were available on release day; other clients and some extensions follow within days.
The migration guide is short, and most of it is about one thing: the storage format. The checks below were run on 2026-10-22 with the DuckDB 2.0.0 and 1.5.6 Python packages on Windows.
The storage format only moves forward
Every new database file created by 2.0.0 uses storage version 69, which earlier versions cannot read. The reverse works: 2.0.0 opens and writes existing files and keeps their storage version, so they stay readable by the older release. The probe confirmed all three statements.
| Step | Result |
|---|---|
| 2.0.0 creates new.db with defaults; 1.5.6 opens it | IO Error: Trying to read a database file with version number 69, but we can only read versions between 64 and 68 |
| 2.0.0 creates compat.db with storage_compatibility_version v1.5.6; 1.5.6 opens it | Opens and reads the table |
| 1.5.6 creates a file; 2.0.0 inserts and creates a table; 1.5.6 reopens it | Opens; both changes are visible |
This matters for any deployment where processes of different versions share files: a scheduled job upgraded first creates a file the dashboards still on 1.5 cannot open. Upgrade the readers first. Where that is impossible, have upgraded writers create files with an explicit storage version, through the -storage-version CLI argument, the storage_compatibility_version setting or the STORAGE_VERSION option of ATTACH, until the last reader moves. To convert an old file to the new format, export it with the old version and import it with the new one.
Illustrative python, checked for syntax; adapt names and storage to your system.
import duckdb
# Writers upgraded before readers: keep new files readable by 1.5.x until every reader is on 2.0
con = duckdb.connect("daily.db", config={"storage_compatibility_version": "v1.5.6"})
con.execute("CREATE TABLE IF NOT EXISTS t (x INT)")
con.close()Query changes that can break code
| Change | Observed on 2.0.0 | Fix or bridge |
|---|---|---|
| Single-arrow lambdas disabled by default | x -> x + 1 raises a Binder Error naming the new syntax | lambda x: x + 1; SET lambda_syntax = 'enable_single_arrow' until 2.1 removes it |
| Integer division and modulo by zero | SELECT 1 // 0 and 1 % 0 raise Invalid Input Error | TRY(...) per expression, or SET error_on_division_by_zero = false; 1 / 0 still returns inf |
| preserve_identifier_case is now text | Reports preserve_case | Old true and false values map to preserve_case and lowercase |
| S3 settings in URL parameters | Not tested here; documented as disabled by default | Use secrets; SET s3_enable_deprecated_url_parameters = true as a bridge |
| New PEG parser | All probe queries parsed unchanged | INSTALL AND LOAD legacy_parser and SET enable_legacy_parser = true if one fails |
Search query text in code and in saved notebooks for -> inside list functions and for // and % on columns that can be zero. Both changes fail loudly, which is better than silently returning NULL, but they fail at run time, so test with representative data rather than relying on parsing.
Triggers, and what they do not do yet
Triggers arrive with statement-level and row-level forms and transition tables. The probe created an AFTER UPDATE ... FOR EACH STATEMENT trigger with REFERENCING OLD TABLE AS o NEW TABLE AS n that writes an audit row per changed account; an update of two rows produced two audit rows with the old and new balances. Trying to create a BEFORE UPDATE ... FOR EACH ROW trigger failed with Not implemented Error: BEFORE FOR EACH ROW triggers are not yet supported, consistent with the limitations the announcement lists. Use triggers for audit tables and incrementally maintained aggregates; keep validation that must run before a write in the code that writes.
Quack: a server you now have to secure
DuckDB has been in-process since its first release. With Quack, any DuckDB process can serve its databases to remote clients, started with CALL quack_serve(token = ...) or duckdb -serve. The announcement is explicit about exposure: the server listens on localhost by default; to accept other machines, keep it on localhost behind a TLS-terminating reverse proxy, and treat the option that exposes the server directly as being for testing only. That makes the security checklist familiar: TLS at the proxy, a token from a secret store rather than the command line, network policy that allows only the clients that need it, and the same review you would give any new database endpoint. The same release also lets an organisation host signed extension repositories; when a repository is created, DuckDB pins its public keys and prints their SHA-256 fingerprints, which should be compared with fingerprints published out of band.
An upgrade order for a fleet
- Inventory every DuckDB process and client library version, including notebooks, CI jobs and embedded uses.
- Run the workload on a shadow system with 2.0.0, as the migration guide recommends, and fix lambda, division and parser failures.
- Upgrade every reader of shared files first; keep writers on their old version or set the storage compatibility version.
- Upgrade writers; remove the compatibility setting once no 1.x process opens the files.
- Convert long-lived files to the new format with export and import when you want the new storage features.
- If the workload is mission-critical, wait for the first patch release, which the announcement expects in November.
Failure modes
- A writer upgraded before its readers, producing files the readers cannot open.
- Pinning only the Python package while the CLI on the same host upgrades, or the reverse.
- Queries that divided by zero and returned NULL now failing at run time in a nightly job.
- A Quack server exposed directly for convenience and forgotten.
- Assuming every client language is available on release day; the announcement says some follow later.
Verification
Keep the probe as a test: create a file with the production writer configuration, open it with every reader version you run, and fail the build if any cannot. Add the lambda and division cases to the query test suite, and check that the CLI and every client library report the version you expect. For how the new CLI behaves when coding agents drive it, see the guide to CLI output for coding agents.
Sources and scope
Checked against these sources on 22 October 2026. Event: DuckDB 2.0.0 LTS release, 21 October 2026; recheck it before acting. Examples and figures are illustrative, not measurements.
Upgrade readers before writers or set the storage compatibility version, fix single-arrow lambdas and division by zero, keep Quack behind a TLS proxy, and test files across every version you run.