agalwood/Motrix · critical · StaleSchemaError
activity_schema_missing
activity_schema_missing
Error message
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.
Action: delete the database file and restart the app to recreate it on the current v1 schema.
rm '${dbPath}' What it means
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).
Source
Thrown at src/core/session/migrations/index.ts:462
typeof activityMeta.generation === 'string' &&
activityMeta.generation.length > 0 &&
typeof activityMeta.tracking_started_at === 'bigint' &&
activityMeta.tracking_started_at > 0n &&
activityMeta.tracking_started_at <= BigInt(Number.MAX_SAFE_INTEGER) &&
typeof activityMeta.revision === 'bigint' &&
activityMeta.revision >= 0n &&
activityMeta.revision <= BigInt(Number.MAX_SAFE_INTEGER - 2) &&
(activityMeta.coverage_gap_at === null ||
(typeof activityMeta.coverage_gap_at === 'bigint' &&
activityMeta.coverage_gap_at > 0n &&
activityMeta.coverage_gap_at <= BigInt(Number.MAX_SAFE_INTEGER)))
if (
!hasCanonicalActivityObjects ||
activityTriggers.length !== 0 ||
!hasNoUnexpectedUniqueActivityIndexes ||
!hasActivityMeta
) {
throw new StaleSchemaError('activity_schema_missing', dbPath)
}
}
for (const m of MIGRATIONS) {
if (m.version > current) {
db.transaction(() => {
m.up(db)
db.prepare(
'INSERT INTO schema_version (version, applied_at) VALUES (?, ?)'
).run(m.version, Date.now())
})()
}
}
validateCanonicalV3(db)
}
View on GitHub (pinned to 1a708ee577)
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.
Example fix
# before: activity tables drifted / singleton meta missing / stray unique index
# after
rm '${dbPath}'
# restart; migrate() builds activity tables and inserts the id=1 meta row Defensive patterns
Strategy: try-catch
Validate before calling
function activitySchemaIsCanonical(db: import('better-sqlite3').Database): boolean {
// mirror of migrate()'s check, simplified
const triggers = db.prepare("SELECT 1 FROM sqlite_master WHERE type='trigger' AND tbl_name IN ('task_activity_events','task_activity_meta')").get();
if (triggers) return false;
const meta = db.prepare('SELECT id, generation, tracking_started_at, revision FROM task_activity_meta').get() as any;
return !!meta && meta.id === 1 && typeof meta.generation === 'string' && meta.generation.length > 0;
} 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 === 'activity_schema_missing') {
// reset DB; or DROP activity triggers/non-PK unique indexes and reseed task_activity_meta singleton
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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'.
Common situations: 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.
Related errors
- inherited_schema_missing
- canonical_task_columns_missing
- inspector_activity_schema_missing
- foreign_key_violation
- legacy_table_present
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/c8b11151b5b9bea9.
Report an issue: GitHub.