thedotmack/claude-mem · critical

schema v46: . row is REAL and unrecoverably rounded

Error message

schema v46: ${target.table}.${target.column} row ${row.row_id} is REAL and unrecoverably rounded

What it means

The v46 migration converts sync-revision columns to TEXT affinity. A row whose value is stored as SQLite REAL has already lost precision (uint64 revisions cannot round-trip through a float), so the migration refuses to proceed rather than copying a corrupted revision.

Solutions

  1. Identify the row: SELECT CAST(id AS TEXT), typeof(sync_rev) FROM <table> WHERE typeof(sync_rev)='real'
  2. Restore a pre-migration database backup
  3. Manually repair the row's revision to its correct integer-as-text value if known, then retry
  4. Report as a bug; the value is unrecoverable from the DB alone

Example fix

-- repair a REAL-stored revision to canonical text
UPDATE sdk_sessions SET sync_rev = '42' WHERE id = 7 AND typeof(sync_rev) = 'real';
Defensive patterns

Strategy: try-catch

Validate before calling

const bad = db.query("SELECT COUNT(*) c FROM sdk_sessions WHERE typeof(sync_rev)='real'").get();
if (bad.c > 0) console.error(`${bad.c} REAL-stored revisions need repair before upgrade`);

Try / catch

try { store = new SessionStore(dbPath); } catch (e) {
  if (String(e.message).includes('unrecoverably rounded')) {
    restoreBackup(dbPath); // revision data is already corrupted
  } else throw e;
}

Prevention

When it happens

Trigger: A row in the target table whose typeof(column) === 'real' is encountered while streaming rows during ensureSyncRevisionTextAffinity.

Common situations: A previous bug or external tool (e.g. writing the revision with a driver that binds JS numbers) stored the revision as a float; manual UPDATE statements that set the revision to a decimal literal.

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/b83edffb164119c7. Report an issue: GitHub.

Appendix: source

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

    }

    const tx = this.db.transaction(() => {
      for (const target of targets) {
        const columns = this.db.query(`PRAGMA table_info(${target.table})`).all() as TableColumnInfo[];
        const source = columns.find(info => info.name === target.column);
        if (!source) {
          throw new Error(`schema v46: missing ${target.table}.${target.column}`);
        }

        for (const raw of streamRows(this.db.query(`
          SELECT CAST(id AS TEXT) AS row_id,
                 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;

View on GitHub (pinned to d8bc9755e7)