thedotmack/claude-mem · critical

schema v46: . row is not a positive canonical uint64…

Error message

schema v46: ${target.table}.${target.column} row ${row.row_id} is not a positive canonical uint64 revision

What it means

The v46 migration validates every revision value with assertCanonicalDecimal(..., { positive: true }) — it must be a positive canonical decimal uint64 (no leading zeros, no signs). A value failing this check means the revision column holds invalid data, so the migration aborts.

Solutions

  1. Locate bad rows: SELECT CAST(id AS TEXT), sync_rev FROM <table> WHERE typeof(sync_rev)='text' AND (sync_rev GLOB '*[^0-9]*' OR sync_rev GLOB '0*' OR sync_rev = '')
  2. Correct the revision to the expected positive canonical decimal string
  3. Restore a backup taken before migration
  4. Report as a bug including the offending value

Example fix

// before (invalid)
sync_rev = '007'
// after
sync_rev = '7'
Defensive patterns

Strategy: validation

Validate before calling

function isCanonicalUint64(v: string) {
  return /^[1-9][0-9]*$/.test(v) && BigInt(v) <= 18446744073709551615n;
}
const bad = db.query("SELECT sync_rev FROM sdk_sessions WHERE typeof(sync_rev)='text'").all()
  .filter(r => !isCanonicalUint64(r.sync_rev));

Try / catch

try { store = new SessionStore(dbPath); } catch (e) {
  if (String(e.message).includes('not a positive canonical uint64')) {
    fixOrRestoreRevisionRows(dbPath);
  } else throw e;
}

Prevention

When it happens

Trigger: A row whose CAST(column AS TEXT) revision is e.g. '0', '-5', '007', 'abc', or exceeds uint64 range when ensureSyncRevisionTextAffinity streams rows.

Common situations: Rows written by an older buggy version; manual edits setting sync_rev to 0 or a negative number; values produced by float rounding then stored as text.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/9ba679c8845eba48. Report an issue: GitHub.

Appendix: source

Thrown at src/services/sqlite/SessionStore.ts:730

                 typeof(${target.column}) AS storage_type,
                 CAST(${target.column} AS TEXT) AS revision
          FROM ${target.table}
        `))) {
          const row = raw as { row_id: string; storage_type: string; revision: string | null };
          if (row.storage_type === 'real') {
            throw new Error(
              `schema v46: ${target.table}.${target.column} row ${row.row_id} is REAL and unrecoverably rounded`
            );
          }
          if (row.storage_type !== 'integer' && row.storage_type !== 'text') {
            throw new Error(
              `schema v46: ${target.table}.${target.column} row ${row.row_id} has unsupported ${row.storage_type} storage`
            );
          }
          try {
            assertCanonicalDecimal(row.revision, { positive: true });
          } catch {
            throw new Error(
              `schema v46: ${target.table}.${target.column} row ${row.row_id} is not a positive canonical uint64 revision`
            );
          }
        }

        if (isText(source)) continue;
        if (columns.some(info => info.name === target.temporary)) {
          throw new Error(`schema v46: unexpected temporary column ${target.table}.${target.temporary}`);
        }

        this.db.run(
          `ALTER TABLE ${target.table} ADD COLUMN ${target.temporary} TEXT NOT NULL DEFAULT '1'`
        );
        this.db.run(
          `UPDATE ${target.table} SET ${target.temporary} = CAST(${target.column} AS TEXT)`
        );
        const mismatch = this.db.prepare(`
          SELECT CAST(id AS TEXT) AS row_id

View on GitHub (pinned to d8bc9755e7)