fix/duckdb/failed-to-load-metadata-pointer
DuckDB error

Failed to load metadata pointer

Updated Aug 27, 20265-min read
TL;DR

DuckDB hit an internal assertion while reading the metadata block chain of your .duckdb file. In practice this almost always means the file is corrupt, usually because a previous write, checkpoint, or WAL replay was interrupted before it finished.

  • ·The last process holding the file was OOM-killed, kill -9'd, or crashed mid-write
  • ·The disk holding the database ran out of space during a checkpoint
  • ·The file was copied, rsynced, or moved while another process had it open for writes
  • ·You are on an older DuckDB point release with a known storage-corruption bug
  • ·A .wal sibling file exists and is being replayed into an already-inconsistent main file
CHECK FIRSTCopy the .duckdb file and its .wal sibling somewhere safe before you do anything else. The recovery steps below can make things worse, and this is your one chance to keep the original bytes.

What you're seeing

The error fires the moment you connect to a persisted database file. The id, idx, and ptr numbers change per file, but the message shape is stable across clients.

text
InternalException: INTERNAL Error: Failed to load metadata pointer (id 189, idx 48, ptr 3458764513820541117)

This error signals an assertion failure within DuckDB. This usually occurs due to unexpected conditions or errors in the program's logic.
For more information, see https://duckdb.org/docs/dev/internal_errors

# Go client variant (via go-duckdb):
duckdb error: INTERNAL Error: Failed to load metadata pointer (id 4950, idx 61, ptr 4395513236313609046)

# CLI variant:
Error: INTERNAL Error: Failed to load metadata pointer (id 12, idx 3, ptr 0)
Also seen as: duckdb failed to load metadata pointer, Failed to load the metadata pointer, INTERNAL Error: Failed to load metadata pointer, InternalException Failed to load metadata pointer

What's causing this

Ranked most-likely first.

  1. 1

    The last writer was killed before it finished a checkpoint

    A container hit its memory cgroup and got OOM-killed, someone ran kill -9, the machine lost power, or the filesystem was force-remounted. DuckDB's on-disk metadata is a linked chain of blocks, and a half-written checkpoint leaves a pointer that references a block that was never flushed. The next open reads that pointer and asserts.

  2. 2

    The volume ran out of space during a write

    This is DuckDB issue #9667 and the pattern is well-known. A large INSERT or CHECKPOINT fills the disk, the write returns short, DuckDB proceeds anyway, and the file is now inconsistent. The failure shows up on the next open, not the write that caused it.

  3. 3

    The file was copied while a live writer had it open

    cp, rsync, docker cp, or a scheduled backup that grabbed the .duckdb file mid-transaction produces a torn snapshot. The metadata blocks in the copy point at data blocks that were not yet flushed. This is not a DuckDB bug, it is how any read-uncoordinated snapshot of an active database file behaves.

  4. 4

    You are on an older DuckDB release with a fixed storage bug

    Several DuckDB releases across 0.9, 0.10, and early 1.x lines had bugs where WAL replay or checkpoint could produce an invalid metadata pointer under specific workloads. If the file was written by an old version, upgrading and reopening may or may not read past the damage, but it stops the corruption from recurring.

  5. 5

    The .wal file is being replayed into an already-broken main file

    On open, DuckDB replays the write-ahead log into the main file before it lets you query. If either the main file's metadata chain or the WAL itself is inconsistent, replay can surface the error even when the base file alone might be readable.

How to fix it

Step 1: preserve the original file before you touch anything

None of the recovery steps below are guaranteed. Some can make things worse. Copy the database file and its .wal sibling somewhere read-only before you experiment, so you always have the exact bytes to hand to a support channel or a later attempt.

bash
cp -a my_database.duckdb /tmp/rescue/my_database.duckdb.bak
cp -a my_database.duckdb.wal /tmp/rescue/my_database.duckdb.wal.bak 2>/dev/null || true

Step 2: try opening without the WAL

If the WAL replay is the part that trips the assertion, opening the base file alone gives you the last checkpointed state. You lose any transactions that were only in the WAL, but you may get a working database. Move the .wal aside, do not delete it.

bash
mv my_database.duckdb.wal my_database.duckdb.wal.aside
duckdb my_database.duckdb 'SELECT 42;'

Step 3: try opening with the latest DuckDB

If the file was written by an older release with a fixed storage bug, a current DuckDB may be able to read past the damage. Install the latest stable release and try the connect again. This is also worth trying in read-only mode.

bash
# CLI, latest release:
duckdb -readonly my_database.duckdb

# Python:
# pip install --upgrade duckdb
# con = duckdb.connect('my_database.duckdb', read_only=True)

Step 4: if it opens, EXPORT DATABASE immediately

Assume the connection is fragile. Get the data out into a portable format before doing anything else. EXPORT DATABASE writes one Parquet file per table plus a schema.sql, and IMPORT DATABASE rebuilds a fresh, clean file from it.

sql
EXPORT DATABASE '/tmp/rescue/export' (FORMAT PARQUET);

-- Then, in a fresh DuckDB session:
ATTACH '/tmp/rescue/clean.duckdb' AS clean;
USE clean;
IMPORT DATABASE '/tmp/rescue/export';

Step 5: if a full export fails, dump table by table

The corruption may only affect one region of the file. Query information_schema for the table list, then COPY each one to Parquet individually. Any table that errors is one you skip and reingest from source; the rest you keep.

sql
SELECT table_name FROM information_schema.tables WHERE table_schema = 'main';

-- For each surviving table:
COPY my_table TO '/tmp/rescue/my_table.parquet' (FORMAT PARQUET);

Step 6: if nothing opens the file, restore from backup or reingest

DuckDB does not ship a repair tool for metadata-chain corruption. If steps 2 through 5 all fail, the file is not recoverable through DuckDB itself. Restore from your last known-good backup, or rebuild the database from source Parquet, CSV, or upstream tables.

bash
# Rebuild from source data, whatever that looks like:
duckdb fresh.duckdb <<'SQL'
CREATE TABLE events AS SELECT * FROM 's3://my-bucket/events/*.parquet';
SQL

Prevention

Never SIGKILL a DuckDB process holding a file open for writes, and never let the runtime do it either. In Docker and Kubernetes, set DuckDB's memory_limit safely below the container's memory ceiling so the OOM killer never fires. A 12 GB container should run DuckDB with an 8 to 10 GB memory_limit, not 12.

Never copy, rsync, or snapshot a .duckdb file that has a live writer. Close all connections and CHECKPOINT before you copy, or use EXPORT DATABASE for a snapshot-consistent, portable backup that survives version upgrades.

Monitor free space on the volume holding the database and its temp_directory. A write that runs out of disk mid-checkpoint is one of the most reliable ways to corrupt a DuckDB file, and the failure shows up on the next open rather than the write itself.

Stay on a current DuckDB release. Storage-corruption bugs get fixed release-over-release, and sticking on an old point version leaves you exposed to failure modes that no longer exist upstream.

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