{"record":{"id":"f472e477761cb986","repo":"agalwood/Motrix","slug":"legacy-table-present","errorCode":"legacy_table_present","errorMessage":"The versioned database schema is incompatible with this build: legacy table `task_metadata` is still present. 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: legacy table `task_metadata` is still present\\. 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 '(.+?)'","errorType":"exception","errorClass":"StaleSchemaError","httpStatus":null,"severity":"critical","filePath":"src/core/session/migrations/index.ts","lineNumber":343,"sourceCode":"      db\n        .prepare(\n          \"SELECT 1 FROM sqlite_master WHERE type='table' AND name='task_metadata'\"\n        )\n        .get() !== undefined\n    const hasTransferSchema =\n      db\n        .prepare(\n          \"SELECT 1 FROM sqlite_master WHERE type='table' AND name='transfer_totals'\"\n        )\n        .get() !== undefined &&\n      db\n        .prepare(\n          \"SELECT 1 FROM sqlite_master WHERE type='table' AND name='transfer_buckets'\"\n        )\n        .get() !== undefined\n    const dbPath = (db as unknown as { name: string }).name\n    if (hasLegacySchema) {\n      throw new StaleSchemaError('legacy_table_present', dbPath)\n    }\n    if (!hasNewSchema) {\n      throw new StaleSchemaError('new_tables_missing', dbPath)\n    }\n    const taskColumns = new Set(\n      (\n        db.prepare('PRAGMA table_info(tasks)').all() as Array<{ name: string }>\n      ).map((column) => column.name)\n    )\n    if (\n      !taskColumns.has('task_type') ||\n      !taskColumns.has('finished_at') ||\n      !taskColumns.has('error_message') ||\n      !taskColumns.has('error_code') ||\n      !taskColumns.has('error_detail_key') ||\n      !taskColumns.has('error_detail_params') ||\n      !taskColumns.has('diagnosis_revision')\n    ) {","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/session/migrations/index.ts#L325-L361","documentation":"StaleSchemaError reason 'legacy_table_present', thrown by Guard B in migrate() at line 342 when sqlite_master still contains a 'task_metadata' table. task_metadata is the pre-Plan-A legacy schema; its presence means this DB predates the Plan A rewrite. Even though schema_version may report >=1, the legacy tables would cause v2+ migrations to crash with cryptic 'no such table' errors, so this guard fails fast.","triggerScenarios":"migrate() Guard B (current > 0) queries for 'task_metadata'; if present, throws at line 343. Fires before any new-table/column checks.","commonSituations":"Long-time user upgrading across the Plan A rewrite boundary; a dev machine with an old DB from before the rewrite; CI fixture generated pre-Plan-A never cleaned up.","solutions":["Delete the DB file (path in dbPath) and restart — migrate() builds the Plan A v1 schema cleanly.","If legacy data must be preserved, run a pre-Plan-A build to export, then import into the new schema after reset.","Do not DROP task_metadata alone — its sibling tables and schema_version row are also legacy; full reset is the supported path."],"exampleFix":"# before: legacy task_metadata table still present\n# after\n  rm '${dbPath}'\n# restart; migrate() creates the Plan A canonical schema from v1","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 === 'legacy_table_present') {\n    // pre-Plan-A DB detected — reset; offer data export via pre-Plan-A build if needed\n  } else throw e;\n}","preventionTips":["During the Plan A rollout, ship a one-time migration prompt rather than relying on the reset message.","Do not preserve pre-Plan-A DB files into the new build's data dir.","Tag CI fixtures with the schema version they target and clean them on version bump."],"tags":["database","sqlite","migration","legacy","plan-a","stale-schema"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}