@@ -281,21 +281,31 @@ function initDb(db: Database.Database): void {
281281 migrateDb ( db ) ;
282282}
283283
284+ /** Safely add a column — ignores "duplicate column name" errors from concurrent workers. */
285+ function safeAddColumn ( db : Database . Database , sql : string ) : void {
286+ try {
287+ db . exec ( sql ) ;
288+ } catch ( err : unknown ) {
289+ if ( err instanceof Error && err . message . includes ( 'duplicate column name' ) ) return ;
290+ throw err ;
291+ }
292+ }
293+
284294function migrateDb ( db : Database . Database ) : void {
285295 const columns = db . prepare ( "PRAGMA table_info(chat_sessions)" ) . all ( ) as { name : string } [ ] ;
286296 const colNames = columns . map ( c => c . name ) ;
287297
288298 if ( ! colNames . includes ( 'model' ) ) {
289- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN model TEXT NOT NULL DEFAULT ''" ) ;
299+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN model TEXT NOT NULL DEFAULT ''" ) ;
290300 }
291301 if ( ! colNames . includes ( 'system_prompt' ) ) {
292- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN system_prompt TEXT NOT NULL DEFAULT ''" ) ;
302+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN system_prompt TEXT NOT NULL DEFAULT ''" ) ;
293303 }
294304 if ( ! colNames . includes ( 'sdk_session_id' ) ) {
295- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN sdk_session_id TEXT NOT NULL DEFAULT ''" ) ;
305+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN sdk_session_id TEXT NOT NULL DEFAULT ''" ) ;
296306 }
297307 if ( ! colNames . includes ( 'project_name' ) ) {
298- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN project_name TEXT NOT NULL DEFAULT ''" ) ;
308+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN project_name TEXT NOT NULL DEFAULT ''" ) ;
299309 // Backfill project_name from working_directory for existing rows
300310 db . exec ( `
301311 UPDATE chat_sessions
@@ -307,33 +317,33 @@ function migrateDb(db: Database.Database): void {
307317 ` ) ;
308318 }
309319 if ( ! colNames . includes ( 'status' ) ) {
310- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN status TEXT NOT NULL DEFAULT 'active'" ) ;
320+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN status TEXT NOT NULL DEFAULT 'active'" ) ;
311321 }
312322 if ( ! colNames . includes ( 'mode' ) ) {
313- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN mode TEXT NOT NULL DEFAULT 'code'" ) ;
323+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN mode TEXT NOT NULL DEFAULT 'code'" ) ;
314324 }
315325 if ( ! colNames . includes ( 'provider_name' ) ) {
316- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN provider_name TEXT NOT NULL DEFAULT ''" ) ;
326+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN provider_name TEXT NOT NULL DEFAULT ''" ) ;
317327 }
318328 if ( ! colNames . includes ( 'provider_id' ) ) {
319- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN provider_id TEXT NOT NULL DEFAULT ''" ) ;
329+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN provider_id TEXT NOT NULL DEFAULT ''" ) ;
320330 }
321331 if ( ! colNames . includes ( 'sdk_cwd' ) ) {
322- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN sdk_cwd TEXT NOT NULL DEFAULT ''" ) ;
332+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN sdk_cwd TEXT NOT NULL DEFAULT ''" ) ;
323333 // Backfill sdk_cwd from working_directory for existing sessions
324334 db . exec ( "UPDATE chat_sessions SET sdk_cwd = working_directory WHERE sdk_cwd = '' AND working_directory != ''" ) ;
325335 }
326336 if ( ! colNames . includes ( 'runtime_status' ) ) {
327- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN runtime_status TEXT NOT NULL DEFAULT 'idle'" ) ;
337+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN runtime_status TEXT NOT NULL DEFAULT 'idle'" ) ;
328338 }
329339 if ( ! colNames . includes ( 'runtime_updated_at' ) ) {
330- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN runtime_updated_at TEXT NOT NULL DEFAULT ''" ) ;
340+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN runtime_updated_at TEXT NOT NULL DEFAULT ''" ) ;
331341 }
332342 if ( ! colNames . includes ( 'runtime_error' ) ) {
333- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN runtime_error TEXT NOT NULL DEFAULT ''" ) ;
343+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN runtime_error TEXT NOT NULL DEFAULT ''" ) ;
334344 }
335345 if ( ! colNames . includes ( 'permission_profile' ) ) {
336- db . exec ( "ALTER TABLE chat_sessions ADD COLUMN permission_profile TEXT NOT NULL DEFAULT 'default'" ) ;
346+ safeAddColumn ( db , "ALTER TABLE chat_sessions ADD COLUMN permission_profile TEXT NOT NULL DEFAULT 'default'" ) ;
337347 }
338348 db . exec ( "CREATE INDEX IF NOT EXISTS idx_sessions_runtime_status ON chat_sessions(runtime_status)" ) ;
339349
@@ -352,7 +362,7 @@ function migrateDb(db: Database.Database): void {
352362 const msgColNames = msgColumns . map ( c => c . name ) ;
353363
354364 if ( ! msgColNames . includes ( 'token_usage' ) ) {
355- db . exec ( "ALTER TABLE messages ADD COLUMN token_usage TEXT" ) ;
365+ safeAddColumn ( db , "ALTER TABLE messages ADD COLUMN token_usage TEXT" ) ;
356366 }
357367
358368 // Ensure tasks table exists for databases created before this migration
@@ -374,10 +384,10 @@ function migrateDb(db: Database.Database): void {
374384 const taskColumns = db . prepare ( "PRAGMA table_info(tasks)" ) . all ( ) as { name : string } [ ] ;
375385 const taskColNames = taskColumns . map ( c => c . name ) ;
376386 if ( ! taskColNames . includes ( 'source' ) ) {
377- db . exec ( "ALTER TABLE tasks ADD COLUMN source TEXT NOT NULL DEFAULT 'user'" ) ;
387+ safeAddColumn ( db , "ALTER TABLE tasks ADD COLUMN source TEXT NOT NULL DEFAULT 'user'" ) ;
378388 }
379389 if ( ! taskColNames . includes ( 'sort_order' ) ) {
380- db . exec ( "ALTER TABLE tasks ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0" ) ;
390+ safeAddColumn ( db , "ALTER TABLE tasks ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0" ) ;
381391 }
382392
383393 // Ensure api_providers table exists for databases created before this migration
@@ -402,19 +412,19 @@ function migrateDb(db: Database.Database): void {
402412 const providerCols = db . prepare ( "PRAGMA table_info(api_providers)" ) . all ( ) as { name : string } [ ] ;
403413 const provColNames = providerCols . map ( c => c . name ) ;
404414 if ( ! provColNames . includes ( 'protocol' ) ) {
405- db . exec ( "ALTER TABLE api_providers ADD COLUMN protocol TEXT NOT NULL DEFAULT ''" ) ;
415+ safeAddColumn ( db , "ALTER TABLE api_providers ADD COLUMN protocol TEXT NOT NULL DEFAULT ''" ) ;
406416 }
407417 if ( ! provColNames . includes ( 'headers_json' ) ) {
408- db . exec ( "ALTER TABLE api_providers ADD COLUMN headers_json TEXT NOT NULL DEFAULT '{}'" ) ;
418+ safeAddColumn ( db , "ALTER TABLE api_providers ADD COLUMN headers_json TEXT NOT NULL DEFAULT '{}'" ) ;
409419 }
410420 if ( ! provColNames . includes ( 'env_overrides_json' ) ) {
411- db . exec ( "ALTER TABLE api_providers ADD COLUMN env_overrides_json TEXT NOT NULL DEFAULT ''" ) ;
421+ safeAddColumn ( db , "ALTER TABLE api_providers ADD COLUMN env_overrides_json TEXT NOT NULL DEFAULT ''" ) ;
412422 }
413423 if ( ! provColNames . includes ( 'role_models_json' ) ) {
414- db . exec ( "ALTER TABLE api_providers ADD COLUMN role_models_json TEXT NOT NULL DEFAULT '{}'" ) ;
424+ safeAddColumn ( db , "ALTER TABLE api_providers ADD COLUMN role_models_json TEXT NOT NULL DEFAULT '{}'" ) ;
415425 }
416426 if ( ! provColNames . includes ( 'options_json' ) ) {
417- db . exec ( "ALTER TABLE api_providers ADD COLUMN options_json TEXT NOT NULL DEFAULT '{}'" ) ;
427+ safeAddColumn ( db , "ALTER TABLE api_providers ADD COLUMN options_json TEXT NOT NULL DEFAULT '{}'" ) ;
418428 }
419429 }
420430
@@ -534,7 +544,7 @@ function migrateDb(db: Database.Database): void {
534544
535545 // Add favorited column to media_generations if missing
536546 try {
537- db . exec ( "ALTER TABLE media_generations ADD COLUMN favorited INTEGER NOT NULL DEFAULT 0" ) ;
547+ safeAddColumn ( db , "ALTER TABLE media_generations ADD COLUMN favorited INTEGER NOT NULL DEFAULT 0" ) ;
538548 } catch {
539549 // Column already exists
540550 }
@@ -690,13 +700,13 @@ function migrateDb(db: Database.Database): void {
690700 const permLinkCols = db . prepare ( "PRAGMA table_info(channel_permission_links)" ) . all ( ) as { name : string } [ ] ;
691701 const permLinkColNames = permLinkCols . map ( c => c . name ) ;
692702 if ( permLinkColNames . length > 0 && ! permLinkColNames . includes ( 'tool_name' ) ) {
693- db . exec ( "ALTER TABLE channel_permission_links ADD COLUMN tool_name TEXT NOT NULL DEFAULT ''" ) ;
703+ safeAddColumn ( db , "ALTER TABLE channel_permission_links ADD COLUMN tool_name TEXT NOT NULL DEFAULT ''" ) ;
694704 }
695705 if ( permLinkColNames . length > 0 && ! permLinkColNames . includes ( 'suggestions' ) ) {
696- db . exec ( "ALTER TABLE channel_permission_links ADD COLUMN suggestions TEXT NOT NULL DEFAULT ''" ) ;
706+ safeAddColumn ( db , "ALTER TABLE channel_permission_links ADD COLUMN suggestions TEXT NOT NULL DEFAULT ''" ) ;
697707 }
698708 if ( permLinkColNames . length > 0 && ! permLinkColNames . includes ( 'resolved' ) ) {
699- db . exec ( "ALTER TABLE channel_permission_links ADD COLUMN resolved INTEGER NOT NULL DEFAULT 0" ) ;
709+ safeAddColumn ( db , "ALTER TABLE channel_permission_links ADD COLUMN resolved INTEGER NOT NULL DEFAULT 0" ) ;
700710 }
701711
702712 // Channel configs table (structured config for channel plugins)
@@ -770,18 +780,18 @@ function migrateDb(db: Database.Database): void {
770780 {
771781 const descCols = db . prepare ( "PRAGMA table_info(cli_tool_descriptions)" ) . all ( ) as { name : string } [ ] ;
772782 if ( ! descCols . some ( c => c . name === 'structured_json' ) ) {
773- db . exec ( "ALTER TABLE cli_tool_descriptions ADD COLUMN structured_json TEXT NOT NULL DEFAULT ''" ) ;
783+ safeAddColumn ( db , "ALTER TABLE cli_tool_descriptions ADD COLUMN structured_json TEXT NOT NULL DEFAULT ''" ) ;
774784 }
775785 }
776786
777787 // Migration: add install_method column to cli_tools_custom
778788 {
779789 const customCols = db . prepare ( "PRAGMA table_info(cli_tools_custom)" ) . all ( ) as { name : string } [ ] ;
780790 if ( ! customCols . some ( c => c . name === 'install_method' ) ) {
781- db . exec ( "ALTER TABLE cli_tools_custom ADD COLUMN install_method TEXT NOT NULL DEFAULT 'unknown'" ) ;
791+ safeAddColumn ( db , "ALTER TABLE cli_tools_custom ADD COLUMN install_method TEXT NOT NULL DEFAULT 'unknown'" ) ;
782792 }
783793 if ( ! customCols . some ( c => c . name === 'install_package' ) ) {
784- db . exec ( "ALTER TABLE cli_tools_custom ADD COLUMN install_package TEXT NOT NULL DEFAULT ''" ) ;
794+ safeAddColumn ( db , "ALTER TABLE cli_tools_custom ADD COLUMN install_package TEXT NOT NULL DEFAULT ''" ) ;
785795 }
786796 }
787797}
0 commit comments