agalwood/Motrix · critical · StaleSchemaError

transfer_tables_missing

transfer_tables_missing

Error message

The versioned database schema is incompatible with this build: expected tables `transfer_totals`/`transfer_buckets` are missing. 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 'transfer_tables_missing', thrown by Guard B at line 364 when either 'transfer_totals' or 'transfer_buckets' is absent. These are part of the inherited Plan A schema; their absence despite schema_version >= 1 indicates the DB is a partial/stale v1.

Source

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

    }
    const taskColumns = new Set(
      (
        db.prepare('PRAGMA table_info(tasks)').all() as Array<{ name: string }>
      ).map((column) => column.name)
    )
    if (
      !taskColumns.has('task_type') ||
      !taskColumns.has('finished_at') ||
      !taskColumns.has('error_message') ||
      !taskColumns.has('error_code') ||
      !taskColumns.has('error_detail_key') ||
      !taskColumns.has('error_detail_params') ||
      !taskColumns.has('diagnosis_revision')
    ) {
      throw new StaleSchemaError('canonical_task_columns_missing', dbPath)
    }
    if (!hasTransferSchema) {
      throw new StaleSchemaError('transfer_tables_missing', dbPath)
    }
    if (!hasTaskFiles) {
      throw new StaleSchemaError('new_tables_missing', dbPath)
    }
    if (current >= 2) {
      const statusSchemas = db
        .prepare(
          `SELECT name, sql FROM sqlite_master
           WHERE type = 'table' AND name IN ('tasks', 'task_instances')`
        )
        .all() as Array<{ name: string; sql: string }>
      if (
        statusSchemas.length !== 2 ||
        statusSchemas.some((table) => !table.sql.includes("'metadata_ready'"))
      ) {
        throw new StaleSchemaError('canonical_task_columns_missing', dbPath)
      }
    }

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Delete the DB and restart — migrate() creates both transfer tables on v1.
  2. If you intentionally disabled transfers in a fork, you must also relax this guard; otherwise reset.
  3. Confirm no schema-bootstrap step was skipped in your build.

Example fix

# before: transfer_totals or transfer_buckets missing
# after
  rm '${dbPath}'
# restart; v1 creates both transfer tables
Defensive patterns

Strategy: try-catch

Validate before calling

function hasTransferTables(db: import('better-sqlite3').Database): boolean {
  const present = (name: string) => db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(name) !== undefined;
  return present('transfer_totals') && present('transfer_buckets');
}

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 === 'transfer_tables_missing') {
    // reset DB; transfer tables will be created on v1
  } else throw e;
}

Prevention

When it happens

Trigger: migrate() Guard B computes hasTransferSchema (transfer_totals AND transfer_buckets both present); if false, throws at line 365. Fires after the tasks-column check, before the task_files check.

Common situations: DB from a build that predated the transfer aggregation tables; an interrupted v1 migration; a custom build that disabled transfer tracking and never created the tables.

Related errors


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