From 3dc4f933705041a2f342050c4ceceeb498edc028 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Tue, 30 Sep 2025 00:21:48 -0700 Subject: [PATCH] fix: handle NULL status values in migration 008 (#578) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Production deployment is failing with CrashLoopBackOff. Migration 008 fails with: ``` ERROR: column "status" of relation "servers" contains null values (SQLSTATE 23502) ``` ## Root Cause The migration extracts the `status` field from an unexpected location in the data structure. ### Actual Data Structure (Before Migration) In the current format, `status` is at the **top level** of the JSON, not nested in official metadata: ```json { "name": "com.example/server", "version": "1.0.0", "status": "active", ← STATUS IS HERE (top level) "_meta": { "io.modelcontextprotocol.registry/official": { "serverId": "uuid", "publishedAt": "2025-09-01T00:00:00Z", "isLatest": true // NO STATUS FIELD HERE } } } ``` For 120 out of 765 servers, the status field is **missing entirely**: ```json { "name": "com.biodnd/agent-ip", "version": "0.1.2", // ← NO STATUS FIELD AT ALL "_meta": { "io.modelcontextprotocol.registry/official": { "serverId": "uuid", "publishedAt": "2025-09-23T09:47:07Z", "isLatest": true // STILL NO STATUS FIELD HERE } } } ``` ### Original Migration Logic ```sql IF official_meta IS NOT NULL THEN UPDATE servers SET status = (official_meta->>'status'), -- Extracts NULL for all servers ``` **Result:** - All 765 servers would have NULL status extracted - Because `official_meta->>'status'` returns NULL (field doesn't exist there) - The migration tries to add `NOT NULL` constraint → fails ### Fixed Migration Logic ```sql IF official_meta IS NOT NULL THEN UPDATE servers SET status = COALESCE(NULLIF(rec.value->>'status', 'null'), 'active'), ``` **Result:** - Extracts from top-level JSON where status actually exists - `NULLIF(rec.value->>'status', 'null')` handles edge case of literal string "null" - `COALESCE(..., 'active')` provides fallback when status is missing - All servers get valid status value ### Impact Breakdown **645 servers with `"status": "active"`:** - Before fix: Would extract NULL → migration fails - After fix: Extracts "active" correctly **120 servers with missing status field:** - Before fix: Would extract NULL → migration fails - After fix: Defaults to 'active' via COALESCE ## Example Servers with Missing Status (First 9 discovered during investigation) - com.biodnd/agent-ip - io.github.jkakar/cookwith-mcp - com.joelverhagen.mcp/Knapcode.SampleMcpServer - io.github.Lyellr88/marm-mcp-server - com.biodnd/agent-press - io.github.timheuer/sampledotnetmcpserver - io.github.CodeCraftersLLC/local-voice-mcp - com.biodnd/agent-fin - io.github.ruvnet/ruv-swarm ## Testing Verified against production data (765 total servers): - ✅ All servers have official metadata - ✅ None have status in official metadata (expected) - ✅ 645 have "status": "active" at top level - ✅ 120 have missing status field (no field present) - ✅ Fix extracts from correct location for all cases ## Impact This unblocks production deployment. Once merged and released as v1.2.1, the migration will: 1. Extract status from the correct location (top-level JSON) 2. Handle missing status fields with sensible defaults 3. Successfully add NOT NULL constraint 4. Allow pods to start normally --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../database/migrations/008_separate_official_metadata.sql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/database/migrations/008_separate_official_metadata.sql b/internal/database/migrations/008_separate_official_metadata.sql index 5bafe17..767a7bb 100644 --- a/internal/database/migrations/008_separate_official_metadata.sql +++ b/internal/database/migrations/008_separate_official_metadata.sql @@ -35,9 +35,10 @@ BEGIN IF official_meta IS NOT NULL THEN -- Update columns with extracted metadata + -- Note: status is at top level in old format, not in official_meta UPDATE servers SET - status = (official_meta->>'status')::VARCHAR(50), + status = COALESCE(NULLIF(rec.value->>'status', 'null'), 'active'), published_at = (official_meta->>'publishedAt')::TIMESTAMP WITH TIME ZONE, updated_at = (official_meta->>'updatedAt')::TIMESTAMP WITH TIME ZONE, is_latest = (official_meta->>'isLatest')::BOOLEAN @@ -46,7 +47,7 @@ BEGIN -- Handle records without official metadata (set defaults) UPDATE servers SET - status = COALESCE((rec.value->>'status'), 'active'), + status = COALESCE(NULLIF(rec.value->>'status', 'null'), 'active'), published_at = NOW(), updated_at = NOW(), is_latest = true