agalwood/Motrix · critical · StaleSchemaError

canonical_task_columns_missing

canonical_task_columns_missing

Error message

The versioned database schema is incompatible with this build: expected canonical task, task-instance, and task-file columns (including `task_type`, `finished_at`, `error_message`, `error_code`, `error_detail_key`, `error_detail_params`, and `diagnosis_revision`), order, constraints, foreign keys, and indexes are invalid. This unpublished build updates the canonical v1 schema directly, so an existing local development database can have a valid version marker while its substantive tables are stale.

Action: delete the database file and restart the app to recreate it on the current v1 schema.
  rm '${dbPath}'

What it means

StaleSchemaError reason 'canonical_task_columns_missing', thrown by assertCanonicalTaskSchema() inside validateCanonicalV3() (post-migration). The tasks/task_instances/task_files tables' DDL does not EXACTLY match V2_TASK_SCHEMA_OBJECTS, or there are indexes/triggers on those tables whose names are not in the expected objects set. Runs at the END of migrate() so it catches both pre-existing drift and botched v2 migration.

Source

Thrown at src/core/session/migrations/index.ts:225

       WHERE tbl_name IN (${CANONICAL_TASK_TABLES.map(() => '?').join(', ')})
         AND (
           type = 'trigger'
           OR (type = 'index' AND sql IS NOT NULL)
         )`
    )
    .all(...CANONICAL_TASK_TABLES) as Array<{ name: string }>

  return explicitIndexesAndTriggers.every((object) =>
    expectedNames.has(object.name)
  )
}

function assertCanonicalTaskSchema(
  db: Database.Database,
  objects: readonly { name: string; sql: string }[]
): void {
  if (!hasExactCanonicalTaskSchema(db, objects)) {
    throw new StaleSchemaError(
      'canonical_task_columns_missing',
      (db as unknown as { name: string }).name
    )
  }
}

function validateCanonicalV3(db: Database.Database): void {
  const dbPath = (db as unknown as { name: string }).name
  assertCanonicalInheritedSchema(db)
  assertCanonicalTaskSchema(db, V2_TASK_SCHEMA_OBJECTS)

  if (!hasExactSchemaObjects(db, V3_SCHEMA_OBJECTS)) {
    throw new StaleSchemaError('inspector_activity_schema_missing', dbPath)
  }

  const unexpectedIndexes = [
    'task_inspector_activity',
    'task_history_events',

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Delete the dev DB and restart to rebuild on the canonical v2 task schema via the migration chain.
  2. Inspect tasks/task_instances/task_files DDL (PRAGMA sqlite_master.sql) to find the drift if you need to preserve data, then re-import after reset.
  3. Confirm v2 migration ran cleanly — check schema_version rows for version=2.

Example fix

# before: task tables drifted from canonical DDL
# after
  rm '${dbPath}'
# restart; migrate() runs v1->v2 cleanly and validateCanonicalV3 passes
Defensive patterns

Strategy: try-catch

Type guard

function isStaleSchemaError(e: unknown): e is StaleSchemaError { return e instanceof StaleSchemaError; }

Try / catch

try {
  migrate(db);
} catch (e) {
  if (e instanceof StaleSchemaError && e.reason === 'canonical_task_columns_missing') {
    // reset DB; or inspect tasks/task_instances/task_files DDL to localize drift
  } else throw e;
}

Prevention

When it happens

Trigger: validateCanonicalV3(db) at line 235 calls assertCanonicalTaskSchema(db, V2_TASK_SCHEMA_OBJECTS); hasExactCanonicalTaskSchema returns false because DDL mismatch OR an unexpected index/trigger name on tasks/task_instances/task_files.

Common situations: Local DB built under an older PR where the task tables had different columns, PK order, CHECK constraints, or WITHOUT ROWID; a v2 migration that partially failed leaving a hybrid table; a debug session that added an index on tasks.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/7d96b69a3a42baab. Report an issue: GitHub.