{"record":{"id":"7d96b69a3a42baab","repo":"agalwood/Motrix","slug":"canonical-task-columns-missing","errorCode":"canonical_task_columns_missing","errorMessage":"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.\n\nAction: delete the database file and restart the app to recreate it on the current v1 schema.\n  rm '${dbPath}'","messagePattern":"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\\.\n\nAction: delete the database file and restart the app to recreate it on the current v1 schema\\.\n  rm '\\$\\{dbPath\\}'","errorType":"exception","errorClass":"StaleSchemaError","httpStatus":null,"severity":"critical","filePath":"src/core/session/migrations/index.ts","lineNumber":225,"sourceCode":"       WHERE tbl_name IN (${CANONICAL_TASK_TABLES.map(() => '?').join(', ')})\n         AND (\n           type = 'trigger'\n           OR (type = 'index' AND sql IS NOT NULL)\n         )`\n    )\n    .all(...CANONICAL_TASK_TABLES) as Array<{ name: string }>\n\n  return explicitIndexesAndTriggers.every((object) =>\n    expectedNames.has(object.name)\n  )\n}\n\nfunction assertCanonicalTaskSchema(\n  db: Database.Database,\n  objects: readonly { name: string; sql: string }[]\n): void {\n  if (!hasExactCanonicalTaskSchema(db, objects)) {\n    throw new StaleSchemaError(\n      'canonical_task_columns_missing',\n      (db as unknown as { name: string }).name\n    )\n  }\n}\n\nfunction validateCanonicalV3(db: Database.Database): void {\n  const dbPath = (db as unknown as { name: string }).name\n  assertCanonicalInheritedSchema(db)\n  assertCanonicalTaskSchema(db, V2_TASK_SCHEMA_OBJECTS)\n\n  if (!hasExactSchemaObjects(db, V3_SCHEMA_OBJECTS)) {\n    throw new StaleSchemaError('inspector_activity_schema_missing', dbPath)\n  }\n\n  const unexpectedIndexes = [\n    'task_inspector_activity',\n    'task_history_events',","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/session/migrations/index.ts#L207-L243","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete the dev DB and restart to rebuild on the canonical v2 task schema via the migration chain.","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.","Confirm v2 migration ran cleanly — check schema_version rows for version=2."],"exampleFix":"# before: task tables drifted from canonical DDL\n# after\n  rm '${dbPath}'\n# restart; migrate() runs v1->v2 cleanly and validateCanonicalV3 passes","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isStaleSchemaError(e: unknown): e is StaleSchemaError { return e instanceof StaleSchemaError; }","tryCatchPattern":"try {\n  migrate(db);\n} catch (e) {\n  if (e instanceof StaleSchemaError && e.reason === 'canonical_task_columns_missing') {\n    // reset DB; or inspect tasks/task_instances/task_files DDL to localize drift\n  } else throw e;\n}","preventionTips":["Never add indexes/triggers to tasks/task_instances/task_files outside the canonical objects list.","When changing task table DDL in a PR, update V2_TASK_SCHEMA_OBJECTS in lockstep.","Reset dev DB after pulling migration changes."],"tags":["database","sqlite","migration","schema","tasks","stale-schema"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}