{"record":{"id":"c8b11151b5b9bea9","repo":"agalwood/Motrix","slug":"activity-schema-missing","errorCode":"activity_schema_missing","errorMessage":"The versioned database schema is incompatible with this build: expected Dashboard activity tables, columns, singleton metadata, foreign-key/trigger policy, or the time-first activity index 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 Dashboard activity tables, columns, singleton metadata, foreign-key/trigger policy, or the time-first activity index 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 '(.+?)'","errorType":"exception","errorClass":"StaleSchemaError","httpStatus":null,"severity":"critical","filePath":"src/core/session/migrations/index.ts","lineNumber":462,"sourceCode":"      typeof activityMeta.generation === 'string' &&\n      activityMeta.generation.length > 0 &&\n      typeof activityMeta.tracking_started_at === 'bigint' &&\n      activityMeta.tracking_started_at > 0n &&\n      activityMeta.tracking_started_at <= BigInt(Number.MAX_SAFE_INTEGER) &&\n      typeof activityMeta.revision === 'bigint' &&\n      activityMeta.revision >= 0n &&\n      activityMeta.revision <= BigInt(Number.MAX_SAFE_INTEGER - 2) &&\n      (activityMeta.coverage_gap_at === null ||\n        (typeof activityMeta.coverage_gap_at === 'bigint' &&\n          activityMeta.coverage_gap_at > 0n &&\n          activityMeta.coverage_gap_at <= BigInt(Number.MAX_SAFE_INTEGER)))\n    if (\n      !hasCanonicalActivityObjects ||\n      activityTriggers.length !== 0 ||\n      !hasNoUnexpectedUniqueActivityIndexes ||\n      !hasActivityMeta\n    ) {\n      throw new StaleSchemaError('activity_schema_missing', dbPath)\n    }\n  }\n\n  for (const m of MIGRATIONS) {\n    if (m.version > current) {\n      db.transaction(() => {\n        m.up(db)\n        db.prepare(\n          'INSERT INTO schema_version (version, applied_at) VALUES (?, ?)'\n        ).run(m.version, Date.now())\n      })()\n    }\n  }\n\n  validateCanonicalV3(db)\n}\n","sourceCodeStart":444,"sourceCodeEnd":479,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/session/migrations/index.ts#L444-L479","documentation":"StaleSchemaError reason 'activity_schema_missing', thrown by Guard B at line 461 when the Dashboard activity schema is invalid on any of four axes: (1) hasExactSchemaObjects(ACTIVITY_SCHEMA_OBJECTS) false (DDL drift on task_activity_events/task_activity_meta), (2) activityTriggers.length !== 0 (any trigger on those tables), (3) an unexpected unique non-PK index exists on task_activity_events, or (4) the task_activity_meta singleton row is missing/malformed (id=1, non-empty generation, valid tracking_started_at, in-range revision, valid coverage_gap_at).","triggerScenarios":"migrate() Guard B (current > 0) performs the composite activity check at lines 395-460; if any of the four conditions fails, throws at line 462 with reason 'activity_schema_missing'.","commonSituations":"DB from a build predating the activity tables; an interrupted activity-schema bootstrap; a debug session that added a trigger or unique index on task_activity_events; corrupted/missing task_activity_meta singleton row from a failed INSERT; bigint range violation in revision/tracking_started_at from a clock-skew event.","solutions":["Delete the DB and restart — migrate() recreates the activity tables and seeds the singleton meta row.","If preserving data, inspect task_activity_meta (should have exactly one row id=1 with valid fields) and DROP any non-PK unique indexes/triggers on task_activity_events.","Confirm PRAGMA foreign_keys and safeIntegers handling are correct in your better-sqlite3 session — bigint range errors can corrupt the meta validation."],"exampleFix":"# before: activity tables drifted / singleton meta missing / stray unique index\n# after\n  rm '${dbPath}'\n# restart; migrate() builds activity tables and inserts the id=1 meta row","handlingStrategy":"try-catch","validationCode":"function activitySchemaIsCanonical(db: import('better-sqlite3').Database): boolean {\n  // mirror of migrate()'s check, simplified\n  const triggers = db.prepare(\"SELECT 1 FROM sqlite_master WHERE type='trigger' AND tbl_name IN ('task_activity_events','task_activity_meta')\").get();\n  if (triggers) return false;\n  const meta = db.prepare('SELECT id, generation, tracking_started_at, revision FROM task_activity_meta').get() as any;\n  return !!meta && meta.id === 1 && typeof meta.generation === 'string' && meta.generation.length > 0;\n}","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 === 'activity_schema_missing') {\n    // reset DB; or DROP activity triggers/non-PK unique indexes and reseed task_activity_meta singleton\n  } else throw e;\n}","preventionTips":["Never add triggers or unique indexes to task_activity_events/task_activity_meta outside the canonical objects.","When changing the activity schema in a PR, update ACTIVITY_SCHEMA_OBJECTS and the meta-seed INSERT in lockstep.","Open better-sqlite3 sessions with .safeIntegers() consistent with the meta validation to avoid bigint range mismatches."],"tags":["database","sqlite","migration","activity","singleton","stale-schema","bigint"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}