thedotmack/claude-mem · error
SyncApply: could not create or adopt a session for…
Error message
SyncApply: could not create or adopt a session for memory_session_id=${memorySessionId} What it means
Thrown by SyncApply.ensureSdkSession after an INSERT ... ON CONFLICT(platform_source, content_session_id) DO NOTHING RETURNING id returned no row (meaning a conflicting row exists) while the follow-up SELECT by that same (platform_source, content_session_id) identity found nothing. That combination should be impossible on a well-formed schema: the row the insert conflicted with cannot be found again. It indicates schema drift (the migration v33 unique index missing or shaped differently), a concurrent writer on another connection deleting the session mid-batch, or platform normalization mismatch between the write and read paths.
Solutions
- Run the pending schema migrations so the v33 ON CONFLICT(platform_source, content_session_id) unique index actually exists.
- Ensure exactly one process runs SyncApply against the database; stop duplicate workers/supervisors.
- Inspect sdk_sessions for the memory_session_id and its (platform_source, content_session_id) neighbors to see what the insert conflicted with.
- As a last resort rebuild the local DB and re-sync from scratch — apply is idempotent.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before enabling sync, confirm the v33 unique index exists so ON CONFLICT adopts correctly.
const idx = db.prepare("SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='sdk_sessions'").all();
if (!idx.some(i => /platform_source.*content_session_id|content_session_id.*platform_source/.test(String(i.name)))) {
throw new Error('sdk_sessions unique index missing — run migrations before syncing');
} Try / catch
try {
syncApply.applyOps(ops, { epoch, requireContiguous: true });
} catch (e) {
if (e instanceof Error && e.message.includes('could not create or adopt a session')) {
// Schema/writer conflict: stop syncing rather than lose session-linked ops.
logger.error('SYNC', 'Halting sync: sdk_sessions adoption failed — check migrations and single-writer invariant', {}, e);
stopSyncLoop();
} else throw e;
} Prevention
- Run schema migrations to completion before starting SyncApply.
- Guarantee a single SyncApply writer per SQLite database (one worker process).
- Never delete sdk_sessions rows externally while sync is active.
When it happens
Trigger: Applying a remote observation/memory op whose memory_session_id is unknown locally, while the sdk_sessions table lacks the expected (platform_source, content_session_id) unique index (old DB never migrated), or a second process (e.g. supervisor + worker both syncing) deletes/rewrites sdk_sessions between the INSERT and SELECT.
Common situations: Upgrading claude-mem with a pre-v33 database where migrations were skipped or partially applied; running two components that both run SyncApply against the same SQLite file; a manual DB cleanup script deleting sessions while sync runs.
Related errors
- Failed to drop dead columns from pending_messages
- Failed to drop worker_pid column from pending_messages
- FTS5 not available — user_prompts_fts skipped (search uses…
- partial canonical mutation snapshot
- Backfill failed
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/7e63e380169ffe7f.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/SyncApply.ts:693
(content_session_id, memory_session_id, project, platform_source, user_prompt, started_at, started_at_epoch, status)
VALUES (?, ?, ?, ?, NULL, ?, ?, 'completed')
ON CONFLICT(platform_source, content_session_id) DO NOTHING
RETURNING id
`).get(content, memorySessionId, project, platform, iso, createdAtEpoch) as { id: number } | null;
let sessionId: number;
if (inserted) {
logger.debug('SYNC_APPLY', 'Created stub sdk_session for remote memory session', {
memorySessionId,
project,
});
sessionId = inserted.id;
} else {
const adopted = this.db.prepare(
'SELECT id FROM sdk_sessions WHERE platform_source = ? AND content_session_id = ?'
).get(platform, content) as { id: number } | undefined;
if (!adopted) {
throw new Error(`SyncApply: could not create or adopt a session for memory_session_id=${memorySessionId}`);
}
sessionId = adopted.id;
}
this.claimParkedTitle(sessionId, SyncApply.parkedTitleMemKey(memorySessionId));
if (contentSessionId) {
this.claimParkedTitle(sessionId, SyncApply.parkedTitleContentKey(platform, contentSessionId));
}
return sessionId;
}
private applyObservation(op: SyncOp, body: Record<string, unknown>, chromaJobs: ChromaJob[]): 'applied' | 'stale' {
const memorySessionId = fieldString(op, body, 'memory_session_id');
const project = fieldString(op, body, 'project');
const type = fieldString(op, body, 'type');
const createdAtEpoch = fieldNumber(op, body, 'created_at_epoch');
if (!memorySessionId || !project || !type || createdAtEpoch === null) {
throw invalidOp(op, 'observation body requires memory_session_id, project, type, created_at_epoch');View on GitHub (pinned to e2d1df569a)