Summary
HINDSIGHT_API_VECTOR_EXTENSION is correctly read and reflected in the startup banner (Extensions: vchord (vector) / scann (vector)), but a separate, unconditional code path in migrations.py still attempts CREATE EXTENSION vector (pgvector) regardless of the configured backend, before the configured-extension dispatch logic ever gets a chance to run. This makes HINDSIGHT_API_VECTOR_EXTENSION unusable for vchord/scann on any host where the bundled pgvector binary itself fails to load (e.g. a glibc mismatch with pg0's pre-built vector.so).
Reproduction
export HINDSIGHT_API_VECTOR_EXTENSION=vchord
export HINDSIGHT_API_LLM_PROVIDER=ollama
export HINDSIGHT_API_LLM_MODEL=<any>
hindsight-api
Startup banner correctly shows:
Extensions: vchord (vector) / native (text)
But migration immediately fails:
pgvector extension is not installed and cannot be installed: (psycopg2.errors.UndefinedFile) could not load library ".../vector.so": ...
[SQL: CREATE EXTENSION vector]
RuntimeError: pgvector extension is required but not installed. Please install it with: CREATE EXTENSION vector;
Same result with HINDSIGHT_API_VECTOR_EXTENSION=scann.
Root cause (traced in hindsight-api-slim==0.7.2, also present in 0.8.3's source as downloaded from PyPI today)
hindsight_api/_vector_index.py has the correct, extension-aware dispatch table:
_EXTENSION_INSTALL_SQL = {
"pgvector": ("CREATE EXTENSION IF NOT EXISTS vector",),
"pgvectorscale": (
"CREATE EXTENSION IF NOT EXISTS vector",
"CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE",
),
"vchord": ("CREATE EXTENSION IF NOT EXISTS vchord CASCADE",),
"scann": (
"CREATE EXTENSION IF NOT EXISTS vector",
"CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE",
),
}
Correctly, vchord has no pgvector dependency.
But hindsight_api/migrations.py's run_migrations() contains an earlier, unconditional block (around line 270-330 in 0.7.2) that runs before os.getenv("HINDSIGHT_API_VECTOR_EXTENSION", ...) is ever read (that read only happens later, around line 339). This earlier block unconditionally checks for and tries to create the literal vector extension:
ext_check = conn.execute(text(
"SELECT extname, nspname FROM pg_extension e "
"JOIN pg_namespace n ON e.extnamespace = n.oid "
"WHERE extname = 'vector'"
)).fetchone()
...
conn.execute(text("CREATE EXTENSION vector"))
This block has no branch on the configured vector backend at all, so it crashes the entire migration before _EXTENSION_INSTALL_SQL's correct, backend-aware logic (used elsewhere, e.g. ensure_vector_extension() at line ~595) ever runs.
Impact
Anyone trying to use a non-pgvector backend (vchord, scann, pgvectorscale) on a host where the bundled pg0 pgvector binary can't load for any reason (glibc mismatch is one concrete case — Debian 12's glibc 2.36 vs. a vector.so requiring 2.38) cannot start Hindsight at all, even though they've explicitly configured a backend that doesn't need pgvector.
Suggested fix
Gate the early unconditional pgvector check/install block on configured_vector_extension() == "pgvector" (or move it after the HINDSIGHT_API_VECTOR_EXTENSION read at line 339, reusing the existing _EXTENSION_INSTALL_SQL dispatch instead of a separate hardcoded check).
Environment
hindsight-api-slim 0.7.2 (installed), confirmed same logic present in 0.8.3 source from PyPI
- Embedded
pg0 PostgreSQL 18.1.0, Debian 12 (glibc 2.36) host
pg0's bundled vector.so requires GLIBC_2.38, which is the proximate trigger, but the real bug is that no HINDSIGHT_API_VECTOR_EXTENSION value avoids this code path
Happy to provide more logs or test a patch.
Filed with the help of Sally Sonnet.
Summary
HINDSIGHT_API_VECTOR_EXTENSIONis correctly read and reflected in the startup banner (Extensions: vchord (vector)/scann (vector)), but a separate, unconditional code path inmigrations.pystill attemptsCREATE EXTENSION vector(pgvector) regardless of the configured backend, before the configured-extension dispatch logic ever gets a chance to run. This makesHINDSIGHT_API_VECTOR_EXTENSIONunusable forvchord/scannon any host where the bundled pgvector binary itself fails to load (e.g. a glibc mismatch withpg0's pre-builtvector.so).Reproduction
Startup banner correctly shows:
But migration immediately fails:
Same result with
HINDSIGHT_API_VECTOR_EXTENSION=scann.Root cause (traced in
hindsight-api-slim==0.7.2, also present in0.8.3's source as downloaded from PyPI today)hindsight_api/_vector_index.pyhas the correct, extension-aware dispatch table:Correctly,
vchordhas no pgvector dependency.But
hindsight_api/migrations.py'srun_migrations()contains an earlier, unconditional block (around line 270-330 in 0.7.2) that runs beforeos.getenv("HINDSIGHT_API_VECTOR_EXTENSION", ...)is ever read (that read only happens later, around line 339). This earlier block unconditionally checks for and tries to create the literalvectorextension:This block has no branch on the configured vector backend at all, so it crashes the entire migration before
_EXTENSION_INSTALL_SQL's correct, backend-aware logic (used elsewhere, e.g.ensure_vector_extension()at line ~595) ever runs.Impact
Anyone trying to use a non-pgvector backend (
vchord,scann,pgvectorscale) on a host where the bundledpg0pgvector binary can't load for any reason (glibc mismatch is one concrete case — Debian 12's glibc 2.36 vs. avector.sorequiring 2.38) cannot start Hindsight at all, even though they've explicitly configured a backend that doesn't need pgvector.Suggested fix
Gate the early unconditional pgvector check/install block on
configured_vector_extension() == "pgvector"(or move it after theHINDSIGHT_API_VECTOR_EXTENSIONread at line 339, reusing the existing_EXTENSION_INSTALL_SQLdispatch instead of a separate hardcoded check).Environment
hindsight-api-slim0.7.2 (installed), confirmed same logic present in 0.8.3 source from PyPIpg0PostgreSQL 18.1.0, Debian 12 (glibc 2.36) hostpg0's bundledvector.sorequiresGLIBC_2.38, which is the proximate trigger, but the real bug is that noHINDSIGHT_API_VECTOR_EXTENSIONvalue avoids this code pathHappy to provide more logs or test a patch.
Filed with the help of Sally Sonnet.