diff --git a/dashboard/src/lib/db.ts b/dashboard/src/lib/db.ts index 3f55f2e43c..dfa52fbffd 100644 --- a/dashboard/src/lib/db.ts +++ b/dashboard/src/lib/db.ts @@ -40,8 +40,22 @@ function createDatabase(): Database.Database { db.pragma('synchronous = NORMAL'); db.pragma('foreign_keys = ON'); - // Run schema initialization - initializeSchema(db); + // Run schema initialization. Same SQLITE_BUSY race as the WAL switch above, + // on the same first-ever-access window (multiple Next.js build workers, no + // existing DB file yet) — busy_timeout already makes SQLite retry for 10s, + // but if every worker is doing the identical CREATE TABLE IF NOT EXISTS + // batch at once, that's not always enough headroom. Same guard: if we still + // hit SQLITE_BUSY after the timeout, check whether another worker's attempt + // already landed the schema (idempotent — IF NOT EXISTS — so "someone else + // created it" is as good as "we created it"); only re-throw if it didn't. + try { + initializeSchema(db); + } catch (err: unknown) { + if ((err as NodeJS.ErrnoException & { code?: string }).code !== 'SQLITE_BUSY') throw err; + const rows = db.pragma("table_info('tasks')") as unknown[]; + if (rows.length === 0) throw err; + // Another worker already created the schema — we're fine. + } return db; }