thedotmack/claude-mem · critical
schema v46: unexpected temporary column
Error message
schema v46: unexpected temporary column ${target.table}.${target.temporary} What it means
Before the v46 migration adds its temporary TEXT column, it verifies the temporary column does not already exist. If it does, a previous migration attempt left the table half-migrated (or was interrupted mid-transaction), and continuing could corrupt data, so it throws.
Solutions
- Restore a database backup from before the interrupted migration
- Inspect the table: PRAGMA table_info(<table>) and drop leftover temp columns only if their data is redundant
- Delete and rebuild the database if the data is expendable
- Report as a bug if it reproduces on a healthy DB
Example fix
-- inspect leftover migration state PRAGMA table_info(sdk_sessions); -- if safe, let migration restart from a clean pre-v46 backup instead of hand-editing
Defensive patterns
Strategy: try-catch
Validate before calling
const cols = db.query('PRAGMA table_info(sdk_sessions)').all();
if (cols.some(c => String(c.name).startsWith('__tmp') || c.name.includes('_tmp_'))) {
console.error('Leftover migration temp column; restore backup');
} Try / catch
try { store = new SessionStore(dbPath); } catch (e) {
if (String(e.message).includes('unexpected temporary column')) {
restoreBackup(dbPath); // do NOT hand-drop the temp column blindly
} else throw e;
} Prevention
- Don't kill the process during startup migrations
- Never restore a DB dump taken mid-migration
- Take backups so interrupted migrations can be rolled back cleanly
When it happens
Trigger: ensureSyncRevisionTextAffinity finds target.temporary (the shadow column added before copy/drop/rename) already present in PRAGMA table_info for the target table.
Common situations: A crashed/killed process left a partially applied migration (SQLite DDL is transactional here, but a foreign/older copy of the DB may contain leftovers); restoring a DB dump taken mid-migration.
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
- Column not found in , skipping rename
- schema v46: failed to copy
- schema v46: missing .
- schema v46: . row is REAL and unrecoverably rounded
- schema v46: . row has unsupported storage
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/bd89408cf46a9bf9.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sqlite/SessionStore.ts:738
);
}
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
FROM ${target.table}
WHERE ${target.temporary} <> CAST(${target.column} AS TEXT)
LIMIT 1
`).get() as { row_id: string } | undefined;
if (mismatch) {
throw new Error(
`schema v46: failed to copy ${target.table}.${target.column} row ${mismatch.row_id} exactly`
);View on GitHub (pinned to d8bc9755e7)