thedotmack/claude-mem · critical
schema v46: failed to copy
Error message
schema v46: failed to copy ${target.table}.${target.column} row ${mismatch.row_id} exactly What it means
After copying revision values into the temporary TEXT column, the migration verifies at least one row matches exactly (temporary <> CAST(column AS TEXT) finds no row). A mismatch means the copy did not preserve the value byte-for-byte, so it refuses to drop/rename columns, preventing silent data loss.
Solutions
- Ensure no other process is writing to the database and retry startup
- Restore a pre-migration backup
- Compare the flagged row's two values to diagnose the affinity behavior
- Report as a bug if it reproduces with exclusive access
Example fix
-- identify the mismatching row SELECT CAST(id AS TEXT) FROM sdk_sessions WHERE __tmp_sync_rev <> CAST(sync_rev AS TEXT) LIMIT 1;
Defensive patterns
Strategy: retry
Validate before calling
// ensure exclusive access before startup
const lock = db.query('PRAGMA database_list').all();
// also verify no other worker holds a write lock:
const busy = db.query('PRAGMA busy_timeout').get(); Try / catch
try { store = new SessionStore(dbPath); } catch (e) {
if (String(e.message).includes('failed to copy') && String(e.message).startsWith('schema v46')) {
await retryWithExclusiveLock(() => new SessionStore(dbPath));
} else throw e;
} Prevention
- Ensure only one process opens the DB during startup/migration
- Stop background workers before upgrading
- Keep a pre-migration backup for rollback
When it happens
Trigger: The post-copy verification SELECT in ensureSyncRevisionTextAffinity returns a row where the temporary column differs from CAST(original AS TEXT).
Common situations: Concurrent writers modifying rows mid-migration; SQLite collation/type affinity quirks altering copied values; an external process touching the DB during startup.
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: missing .
- schema v46: . row is REAL and unrecoverably rounded
- schema v46: . row has unsupported storage
- schema v46: . row is not a positive canonical uint64…
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/869e6edd4ae55fab.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sqlite/SessionStore.ts:754
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`
);
}
this.db.run(`ALTER TABLE ${target.table} DROP COLUMN ${target.column}`);
this.db.run(
`ALTER TABLE ${target.table} RENAME COLUMN ${target.temporary} TO ${target.column}`
);
}
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)')
.run(46, new Date().toISOString());
});
tx();
}
/**
* Canonical-v2 entity heads and durable tombstone queue. Revisions remain
* decimal TEXT so a remote value is never rounded through a JS number.View on GitHub (pinned to d8bc9755e7)