Skip to content

REINDEX on vchordrq fails with "missing chunk ... for toast value" after 0.5.3 -> 1.0.0 upgrade, when table has dead tuples #470

Description

@irishpadres

Summary

REINDEX INDEX on a vchordrq index fails with missing chunk number N for toast value M in pg_toast_X after upgrading from 0.5.3 to 1.0.0, when the underlying table has dead tuples. The embeddings themselves are not corrupt — every row detoasts fine and the same reindex succeeds on 0.5.3. Running VACUUM on the table before the extension upgrade avoids the failure entirely.

Environment

  • PostgreSQL 16.14 (Debian 16.14-1.pgdg13+1), Debian 13 (trixie), amd64
  • vchord 0.5.3 → 1.0.0
  • vector (pgvector) 0.8.1 → 0.8.2
  • Table with a TOASTed vector column and a vchordrq index:
    Table "public.smart_search"
      Column   |     Type     | Storage
    -----------+--------------+----------
     assetId   | uuid         | plain
     embedding | vector(1152) | external
    
    Indexes:
        "smart_search_pkey" PRIMARY KEY, btree ("assetId")
        "clip_index" vchordrq (embedding vector_cosine_ops) WITH (
            residual_quantization = false,
            build.internal.lists = [512],
            spherical_centroids = true,
            build_threads = 4,
            sampling_factor = 1024)
    
    ~270k rows.

Steps to reproduce

  1. On vchord 0.5.3, have a table like the above with some accumulated dead tuples (normal insert/update churn without a recent vacuum/autovacuum pass).
  2. Install vchord 1.0.0, restart postgres, then run:
    ALTER EXTENSION vector UPDATE;
    ALTER EXTENSION vchord UPDATE;
    REINDEX INDEX clip_index;
  3. Observe the reindex fail partway through clustering:
    INFO:  clustering: estimated memory usage is 2.26 GiB
    ERROR:  missing chunk number 0 for toast value 5517461 in pg_toast_17330
    

What we ruled out

  • Not corrupt data. A full scan of every row (SELECT embedding FROM smart_search per row, forcing detoast) completes with zero failures, both before and after the reported error.
  • Not a fluke. Reproduced deterministically from an identical snapshot: same starting DB restored twice, reindex fails every time without a prior vacuum, and succeeds every time with one. The toast value/chunk number differs slightly between runs (5517461/chunk 0, 5517449/chunk 1 in our tests), consistent with it pointing at whichever dead tuples happen to be present rather than a single fixed corrupt row.

Workaround

Running this before ALTER EXTENSION vchord UPDATE (i.e. still on 0.5.3) reliably prevents the failure:

VACUUM (ANALYZE) smart_search;

Note the ordering matters and there's a bit of a catch-22: if you vacuum after the extension is already on 1.0.0 (with the index still in 0.5.3's on-disk format), VACUUM itself errors out:

ERROR:  deserialization: bad version number; after upgrading VectorChord, please use REINDEX

So recovery has to happen in this order: vacuum while still on the old version → upgrade → reindex.

Expected behavior

REINDEX (or the upgrade path in general) shouldn't fail due to ordinary dead tuples in the heap — index builds normally tolerate this via the visibility map / MVCC snapshot rather than needing every dead row's TOAST data to still be intact. This came up via the Immich community-scripts LXC installer, where several users hit it on the 0.5.3 → 1.0.0 bump (community-scripts/ProxmoxVE#15588) and initially assumed it meant DB corruption.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions