{"record":{"id":"7e63e380169ffe7f","repo":"thedotmack/claude-mem","slug":"syncapply-could-not-create-or-adopt-a-session-for","errorCode":null,"errorMessage":"SyncApply: could not create or adopt a session for memory_session_id=${memorySessionId}","messagePattern":"SyncApply: could not create or adopt a session for memory_session_id=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/sync/SyncApply.ts","lineNumber":693,"sourceCode":"        (content_session_id, memory_session_id, project, platform_source, user_prompt, started_at, started_at_epoch, status)\n      VALUES (?, ?, ?, ?, NULL, ?, ?, 'completed')\n      ON CONFLICT(platform_source, content_session_id) DO NOTHING\n      RETURNING id\n    `).get(content, memorySessionId, project, platform, iso, createdAtEpoch) as { id: number } | null;\n\n    let sessionId: number;\n    if (inserted) {\n      logger.debug('SYNC_APPLY', 'Created stub sdk_session for remote memory session', {\n        memorySessionId,\n        project,\n      });\n      sessionId = inserted.id;\n    } else {\n      const adopted = this.db.prepare(\n        'SELECT id FROM sdk_sessions WHERE platform_source = ? AND content_session_id = ?'\n      ).get(platform, content) as { id: number } | undefined;\n      if (!adopted) {\n        throw new Error(`SyncApply: could not create or adopt a session for memory_session_id=${memorySessionId}`);\n      }\n      sessionId = adopted.id;\n    }\n\n    this.claimParkedTitle(sessionId, SyncApply.parkedTitleMemKey(memorySessionId));\n    if (contentSessionId) {\n      this.claimParkedTitle(sessionId, SyncApply.parkedTitleContentKey(platform, contentSessionId));\n    }\n    return sessionId;\n  }\n\n  private applyObservation(op: SyncOp, body: Record<string, unknown>, chromaJobs: ChromaJob[]): 'applied' | 'stale' {\n    const memorySessionId = fieldString(op, body, 'memory_session_id');\n    const project = fieldString(op, body, 'project');\n    const type = fieldString(op, body, 'type');\n    const createdAtEpoch = fieldNumber(op, body, 'created_at_epoch');\n    if (!memorySessionId || !project || !type || createdAtEpoch === null) {\n      throw invalidOp(op, 'observation body requires memory_session_id, project, type, created_at_epoch');","sourceCodeStart":675,"sourceCodeEnd":711,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sync/SyncApply.ts#L675-L711","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before enabling sync, confirm the v33 unique index exists so ON CONFLICT adopts correctly.\nconst idx = db.prepare(\"SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='sdk_sessions'\").all();\nif (!idx.some(i => /platform_source.*content_session_id|content_session_id.*platform_source/.test(String(i.name)))) {\n  throw new Error('sdk_sessions unique index missing — run migrations before syncing');\n}","typeGuard":null,"tryCatchPattern":"try {\n  syncApply.applyOps(ops, { epoch, requireContiguous: true });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('could not create or adopt a session')) {\n  // Schema/writer conflict: stop syncing rather than lose session-linked ops.\n    logger.error('SYNC', 'Halting sync: sdk_sessions adoption failed — check migrations and single-writer invariant', {}, e);\n    stopSyncLoop();\n  } else throw e;\n}","preventionTips":["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."],"tags":["sync","sqlite","upsert","schema-migration","race-condition"],"backgroundTag":"upsert-conflict-race","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}