Skip to content

fix(kanban): make column migrations idempotent under concurrent access - #21529

Closed
luyao618 wants to merge 1 commit into
NousResearch:mainfrom
luyao618:fix/kanban-migration-idempotent-add-column
Closed

fix(kanban): make column migrations idempotent under concurrent access#21529
luyao618 wants to merge 1 commit into
NousResearch:mainfrom
luyao618:fix/kanban-migration-idempotent-add-column

Conversation

@luyao618

@luyao618 luyao618 commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Make _migrate_add_optional_columns() safe under concurrent execution by wrapping each ALTER TABLE ADD COLUMN in a try/except helper (_safe_add_column()).

Problem

When two gateway processes run migrations concurrently (e.g. during a restart), both read the same PRAGMA table_info snapshot before either modifies the schema. Both pass the if col not in cols guard and attempt the same ALTER TABLE ADD COLUMN, causing:

sqlite3.OperationalError: duplicate column name: max_retries

This crashes the kanban dispatcher on every tick, preventing it from starting.

All 14 ALTER TABLE ADD COLUMN statements in the migration were vulnerable.

Fix

Introduce _safe_add_column(conn, table, column, typedef) that catches sqlite3.OperationalError on duplicate column — the standard SQLite idiom since ADD COLUMN IF NOT EXISTS is not supported. All 14 ALTER TABLE statements now use this helper.

The helper returns bool so callers that need to conditionally run follow-up logic (e.g. data copy from legacy columns) can check whether the column was actually added.

Testing

  • All existing kanban tests pass (test_kanban_db.py: 60 passed, test_kanban_core_functionality.py: 148 passed)
  • The fix is inherently safe: catching OperationalError on a duplicate column name is a no-op for the already-correct schema

Closes #21503

The `_migrate_add_optional_columns()` function reads the column list once
via `PRAGMA table_info` and guards each `ALTER TABLE ADD COLUMN` with an
`if col not in cols` check.  When two gateway processes run the migration
concurrently (e.g. during a restart), both snapshots see the old schema
and both attempt the same ALTER TABLE — the second one crashes with
`sqlite3.OperationalError: duplicate column name`.

Introduce a `_safe_add_column()` helper that wraps each ALTER TABLE in a
try/except for `sqlite3.OperationalError`, making every individual
statement idempotent regardless of snapshot freshness.  SQLite lacks
`ADD COLUMN IF NOT EXISTS`, so this is the standard approach.

All 14 ALTER TABLE ADD COLUMN statements in the migration (across both
the `tasks` and `task_events` tables) now use the helper.

Closes NousResearch#21503
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/tools Tool registry, model_tools, toolsets labels May 7, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Closing as superseded. PR #22627 (already merged) addressed the same duplicate-column race with _add_column_if_missing() — slightly stricter than this PR's _safe_add_column (which catches all OperationalErrors, not just duplicate-column ones). The complementary half (skipping the redundant init_db() in the dispatcher) just merged via PR #22994. Thanks @luyao618!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kanban migration crashes on duplicate column: ALTER TABLE ADD COLUMN not idempotent

3 participants