From 470df7fb9f67253a638c51f955a5e1d4bd01532f Mon Sep 17 00:00:00 2001 From: Mannu Date: Sat, 30 May 2026 00:49:43 +0530 Subject: [PATCH] Ensure pgvector extension in migrator + add pgvector diagnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The baseline schema needs the `vector` extension (memories.vision_embedding), but nothing created it on deploy — a fresh DB hit "could not access file vector" and migrations failed. - migrate.ts now runs CREATE EXTENSION IF NOT EXISTS vector (superuser) before applying migrations, with a clear error if the Postgres image lacks pgvector. - /api/debug-migration GET now reports pgvector status (binaryAvailable / installed) so the image/extension can be checked from the browser. Co-Authored-By: Claude Opus 4.8 --- src/app/api/debug-migration/route.ts | 14 +++++++++++++- src/db/migrate.ts | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/app/api/debug-migration/route.ts b/src/app/api/debug-migration/route.ts index d1255a6..83bd886 100644 --- a/src/app/api/debug-migration/route.ts +++ b/src/app/api/debug-migration/route.ts @@ -13,7 +13,19 @@ export async function GET() { const circleTables = await sql.unsafe( `SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND (tablename LIKE 'circle%' OR tablename = 'post_reports') ORDER BY tablename` ); - return NextResponse.json({ migrations, circleTables }); + // pgvector status: is the binary available in this Postgres image, and is + // the extension actually created? (diagnoses "could not access file vector") + const vectorRows = await sql.unsafe( + `SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = 'vector'` + ); + const v = vectorRows[0] as { default_version?: string; installed_version?: string } | undefined; + const pgvector = { + binaryAvailable: !!v, // false → wrong Postgres image (no pgvector) + installed: !!v?.installed_version, // false → needs CREATE EXTENSION vector + availableVersion: v?.default_version ?? null, + installedVersion: v?.installed_version ?? null, + }; + return NextResponse.json({ migrations, circleTables, pgvector }); } catch (err: unknown) { return NextResponse.json({ error: err instanceof Error ? err.message : String(err) }); } diff --git a/src/db/migrate.ts b/src/db/migrate.ts index 2e167d3..5b87763 100644 --- a/src/db/migrate.ts +++ b/src/db/migrate.ts @@ -29,6 +29,30 @@ async function main() { const client = postgres(url, { max: 1 }); try { + // The baseline schema uses pgvector (memories.vision_embedding vector(1536) + // + ivfflat index), so the `vector` extension MUST exist before migrations + // run. We do it here — not in a migration file — because CREATE EXTENSION + // needs a superuser (this runner uses DATABASE_URL_SUPERUSER). Idempotent. + // + // This only succeeds if the Postgres image actually ships the pgvector + // binaries (e.g. the pgvector/pgvector image). A plain `postgres` image + // will fail here with "could not access file vector" — that's an infra + // problem (wrong DB image), and failing loud is correct. + console.log("[migrate] ensuring pgvector 'vector' extension..."); + try { + await client`CREATE EXTENSION IF NOT EXISTS vector`; + console.log("[migrate] pgvector extension ready."); + } catch (extErr) { + console.error( + "[migrate] FAILED to create the pgvector 'vector' extension.\n" + + " → The Postgres image must include pgvector (use the pgvector/pgvector image,\n" + + " e.g. pgvector/pgvector:pg18 to match prod), and the migration connection\n" + + " (DATABASE_URL_SUPERUSER) must be a superuser.", + extErr + ); + throw extErr; + } + console.log("[migrate] applying pending migrations..."); await migrate(drizzle(client), { migrationsFolder: "./drizzle" }); console.log("[migrate] done — schema is up to date.");