From e794f3972601c43c60afc0e47e556923e89f8e1f Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Sun, 1 Mar 2026 00:29:49 -0800 Subject: [PATCH] fix(setup): validate PostgreSQL version and pgvector availability before migrations (#423) * fix(setup): validate PostgreSQL version and pgvector before migrations The setup wizard accepted any DATABASE_URL without checking the server version or pgvector availability. Users who installed PostgreSQL 14 (or any version < 15) got opaque migration failures. Users without pgvector installed hit CREATE EXTENSION errors at runtime. After a successful connection, the wizard now: 1. Queries SHOW server_version and rejects versions below 15 2. Checks pg_available_extensions for the vector extension Both checks provide actionable error messages with platform-specific install guidance. Closes #415 Closes #416 Co-Authored-By: Claude Opus 4.6 * refactor: extract version constant, fix hex escapes in pgvector message - Extract MIN_PG_MAJOR_VERSION constant to avoid magic number - Replace \x20 hex escapes with regular spaces in install guidance Co-Authored-By: Claude Opus 4.6 * fix(setup): use detected PG version in pgvector install instructions The pgvector install hints were hardcoded for PG 16. Since we already parse major_version from SHOW server_version, use it dynamically so users on PG 15 or 17 get correct package names. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- src/setup/wizard.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/setup/wizard.rs b/src/setup/wizard.rs index 7bc85d00..55efb68c 100644 --- a/src/setup/wizard.rs +++ b/src/setup/wizard.rs @@ -543,6 +543,10 @@ impl SetupWizard { } /// Test PostgreSQL connection and store the pool. + /// + /// After connecting, validates: + /// 1. PostgreSQL version >= 15 (required for pgvector compatibility) + /// 2. pgvector extension is available (required for embeddings/vector search) #[cfg(feature = "postgres")] async fn test_database_connection_postgres(&mut self, url: &str) -> Result<(), SetupError> { let mut cfg = PoolConfig::new(); @@ -556,11 +560,57 @@ impl SetupWizard { .create_pool(Some(Runtime::Tokio1), NoTls) .map_err(|e| SetupError::Database(format!("Failed to create pool: {}", e)))?; - let _ = pool + let client = pool .get() .await .map_err(|e| SetupError::Database(format!("Failed to connect: {}", e)))?; + // Check PostgreSQL server version (need 15+ for pgvector) + let version_row = client + .query_one("SHOW server_version", &[]) + .await + .map_err(|e| SetupError::Database(format!("Failed to query server version: {}", e)))?; + let version_str: &str = version_row.get(0); + let major_version = version_str + .split('.') + .next() + .and_then(|v| v.parse::().ok()) + .unwrap_or(0); + + const MIN_PG_MAJOR_VERSION: u32 = 15; + + if major_version < MIN_PG_MAJOR_VERSION { + return Err(SetupError::Database(format!( + "PostgreSQL {} detected. IronClaw requires PostgreSQL {} or later for pgvector support.\n\ + Upgrade: https://www.postgresql.org/download/", + version_str, MIN_PG_MAJOR_VERSION + ))); + } + + // Check if pgvector extension is available + let pgvector_row = client + .query_opt( + "SELECT 1 FROM pg_available_extensions WHERE name = 'vector'", + &[], + ) + .await + .map_err(|e| { + SetupError::Database(format!("Failed to check pgvector availability: {}", e)) + })?; + + if pgvector_row.is_none() { + return Err(SetupError::Database(format!( + "pgvector extension not found on your PostgreSQL server.\n\n\ + Install it:\n \ + macOS: brew install pgvector\n \ + Ubuntu: apt install postgresql-{0}-pgvector\n \ + Docker: use the pgvector/pgvector:pg{0} image\n \ + Source: https://github.com/pgvector/pgvector#installation\n\n\ + Then restart PostgreSQL and re-run: ironclaw onboard", + major_version + ))); + } + self.db_pool = Some(pool); Ok(()) }