From 3e6c96a896286986df531e76d96f633b95af7a19 Mon Sep 17 00:00:00 2001 From: ekko <152005280+EKKOLearnAI@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:03:51 +0800 Subject: [PATCH] fix(session-sync): handle missing estimated_cost_usd column in old Hermes state.db (#312) * fix(session-sync): handle missing estimated_cost_usd column in old Hermes state.db Fixes #308 - "NOT NULL constraint failed: sessions.estimated_cost_usd" Problem: - Old versions of Hermes state.db don't have the estimated_cost_usd column - Session sync would fail when trying to query this column - New sessions also failed to sync because the error blocked the entire sync process Solution: - Dynamically detect if estimated_cost_usd column exists using PRAGMA table_info - For old DBs (no column): return 0 as hardcoded default value - For new DBs (has column): use COALESCE(estimated_cost_usd, 0) to handle NULL values - This ensures backward compatibility with both old and new Hermes installations Changes: - Add PRAGMA table_info check before building SELECT query - Conditionally include estimated_cost_usd column based on schema detection - Ensures session sync works for both old and new Hermes state.db versions Co-Authored-By: Claude Sonnet 4.6 * fix: correct type annotation for PRAGMA table_info result - Change from Array<{ name: string }>[] to Array<{ name: string }> - Fixes TypeScript compilation error - PRAGMA table_info returns an array of objects, not an array of arrays Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- packages/server/src/services/hermes/session-sync.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/server/src/services/hermes/session-sync.ts b/packages/server/src/services/hermes/session-sync.ts index 6ae2215..e3f8315 100644 --- a/packages/server/src/services/hermes/session-sync.ts +++ b/packages/server/src/services/hermes/session-sync.ts @@ -112,6 +112,13 @@ function syncProfileSessions(profile: string): { const db = openHermesStateDb(profile) try { + // Check if sessions table has estimated_cost_usd column + const tableInfo = db.prepare('PRAGMA table_info(sessions)').all() as Array<{ name: string }> + const hasEstimatedCost = tableInfo.some(col => col.name === 'estimated_cost_usd') + + // Build SELECT query - only include estimated_cost_usd if column exists + const estimatedCostCol = hasEstimatedCost ? ', COALESCE(estimated_cost_usd, 0) AS estimated_cost_usd' : ', 0 AS estimated_cost_usd' + // Get all api_server sessions const sessions = db.prepare(` SELECT @@ -128,8 +135,7 @@ function syncProfileSessions(profile: string): { output_tokens, cache_read_tokens, cache_write_tokens, - reasoning_tokens, - estimated_cost_usd + reasoning_tokens${estimatedCostCol} FROM sessions WHERE source = 'api_server' ORDER BY started_at ASC @@ -218,7 +224,7 @@ function syncProfileSessions(profile: string): { cache_read_tokens: hermesSession.cache_read_tokens, cache_write_tokens: hermesSession.cache_write_tokens, reasoning_tokens: hermesSession.reasoning_tokens, - estimated_cost_usd: hermesSession.estimated_cost_usd, + estimated_cost_usd: hermesSession.estimated_cost_usd || 0, last_active: hermesSession.started_at, // Use started_at as fallback since last_active doesn't exist in Hermes state.db preview, })