thedotmack/claude-mem · warning
Failed to drop dead columns from pending_messages
Error message
Failed to drop dead columns from pending_messages
What it means
Migration v31 runs inside a transaction: it deletes finished pending_messages rows (status outside pending/processing) and then drops several dead columns. Any statement failing triggers ROLLBACK and the version marker is not written, so the whole migration retries on next startup. Only the intended cleanup is affected.
Source
Thrown at src/services/sqlite/SessionStore.ts:926
const deadColumns = ['retry_count', 'failed_at_epoch', 'completed_at_epoch'];
const toDrop = deadColumns.filter(name => colNames.has(name));
if (applied && toDrop.length === 0) return;
if (toDrop.length > 0) {
this.db.run('BEGIN TRANSACTION');
try {
this.db.run(`DELETE FROM pending_messages WHERE status NOT IN ('pending', 'processing')`);
for (const colName of toDrop) {
this.db.run(`ALTER TABLE pending_messages DROP COLUMN ${colName}`);
logger.debug('DB', `Dropped dead column ${colName} from pending_messages`);
}
if (!applied) {
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(31, new Date().toISOString());
}
this.db.run('COMMIT');
} catch (error) {
this.db.run('ROLLBACK');
logger.warn('DB', 'Failed to drop dead columns from pending_messages', {}, error instanceof Error ? error : new Error(String(error)));
return;
}
return;
}
if (!applied) {
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(31, new Date().toISOString());
}
}
private initializeSchema(): void {
this.db.run(`
CREATE TABLE IF NOT EXISTS schema_versions (
id INTEGER PRIMARY KEY,
version INTEGER UNIQUE NOT NULL,
applied_at TEXT NOT NULL
)
`);View on GitHub (pinned to 8bc631a71a)
Solutions
- Read the attached error to identify which statement failed
- Upgrade the runtime to SQLite 3.35+ if the error is DROP COLUMN syntax
- Guarantee a writable, unlocked DB with a single worker during startup
- Confirm completion later: version 31 present in schema_versions and the dead columns absent from PRAGMA table_info(pending_messages)
Defensive patterns
Strategy: fallback
Validate before calling
const { v } = db.prepare('SELECT sqlite_version() AS v').get() as { v: string };
const [maj, min] = v.split('.').map(Number);
const ok = maj > 3 || (maj === 3 && min >= 35);
if (!ok) {
// the v31 transaction will roll back and retry each start; upgrade the runtime
} Prevention
- Ensure the DB is writable and unlocked during worker startup so the transaction can commit
- Upgrade SQLite to 3.35+ before expecting dead-column cleanup to stick
- Watch schema_versions for version 31 to confirm the migration eventually lands
When it happens
Trigger: Same constraints as any DROP COLUMN: SQLite below 3.35.0, a locked or read-only DB, or a to-drop column pinned by a trigger/view; the DELETE can also fail on FK enforcement, aborting the batch.
Common situations: Older bundled SQLite; concurrent worker startups on one DB file; migration racing a long write transaction elsewhere.
Related errors
- Failed to drop worker_pid column from pending_messages
- SyncApply: could not create or adopt a session for memory_se
- FTS5 not available — user_prompts_fts skipped (search uses C
- Column ${oldCol} not found in ${table}, skipping rename
- Invalid CLAUDE_MEM_QUEUE_ENGINE=${raw}; expected sqlite or b
AI-assisted analysis of thedotmack/claude-mem@8bc631a71a (2026-08-20).
Data as JSON: /api/errors/3678b05169299db8.
Report an issue: GitHub.