{"record":{"id":"c35081f12c1f58d2","repo":"thedotmack/claude-mem","slug":"syncapply-invalid-op-seq-op-seq-kind-op-kind-origin-op-c35081","errorCode":null,"errorMessage":"SyncApply: invalid op seq=${op.seq} kind=${op.kind} origin=${op.origin_device}/${op.origin_id}: set_title target requires memory_session_id or content_session_id","messagePattern":"SyncApply: invalid op seq=(.+?) kind=(.+?) origin=(.+?)/(.+?): set_title target requires memory_session_id or content_session_id","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/sync/SyncApply.ts","lineNumber":1003,"sourceCode":"    // apply-vs-park. SELECTs in our transaction are authoritative.\n    const memorySessionId = fieldString(op, target, 'memory_session_id');\n    if (memorySessionId) {\n      const session = this.db.prepare(\n        'SELECT id FROM sdk_sessions WHERE memory_session_id = ?'\n      ).get(memorySessionId) as { id: number } | undefined;\n      if (session) {\n        this.db.prepare('UPDATE sdk_sessions SET custom_title = ? WHERE id = ?')\n          .run(customTitle, session.id);\n        return 'applied';\n      }\n      // No such session yet — park until ensureSessionForMemoryId creates it.\n      this.parkTitle(SyncApply.parkedTitleMemKey(memorySessionId), customTitle);\n      return 'applied';\n    }\n\n    const contentSessionId = fieldString(op, target, 'content_session_id');\n    if (!contentSessionId) {\n      throw invalidOp(op, 'set_title target requires memory_session_id or content_session_id');\n    }\n    const platform = normalizePlatformSource(fieldString(op, target, 'platform_source') ?? undefined);\n\n    // (1) Direct match on the session identity.\n    const direct = this.db.prepare(`\n      SELECT id FROM sdk_sessions\n      WHERE COALESCE(NULLIF(platform_source, ''), ?) = ? AND content_session_id = ?\n      LIMIT 1\n    `).get(DEFAULT_PLATFORM_SOURCE, platform, contentSessionId) as { id: number } | undefined;\n    if (direct) {\n      this.db.prepare('UPDATE sdk_sessions SET custom_title = ? WHERE id = ?')\n        .run(customTitle, direct.id);\n      return 'applied';\n    }\n\n    // (2) Via a replicated prompt: when row ops arrived first, the\n    // observation-created stub carries a synthetic content id, but any\n    // replicated prompt knows its real content_session_id AND its local","sourceCodeStart":985,"sourceCodeEnd":1021,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/SyncApply.ts#L985-L1021","documentation":"applySetTitle needs to know which session row to update. It first tries target.memory_session_id; if absent, it falls back to target.content_session_id. When neither identifier is present the op has no addressable target, so invalidOp is thrown rather than attempting a scoped or unbounded update.","triggerScenarios":"A set_title op whose target object has neither memory_session_id nor content_session_id — e.g. target only carries platform_source, or is an empty object.","commonSituations":"A producer that targets sessions by a different key (session_uuid, external id) after a schema change; a hand-built op in tooling/tests that omitted the id; a truncation bug dropping the id field.","solutions":["Include target.memory_session_id (preferred) or target.content_session_id in every set_title op","Confirm the producer resolves the session id before enqueueing the op","Check for op producers using a stale key name and update them to the supported identifiers"],"exampleFix":"// before\ntarget: { platform_source: 'claude-code' }\n// after\ntarget: { memory_session_id: 'mem-123', platform_source: 'claude-code' }","handlingStrategy":"validation","validationCode":"function hasSessionTarget(target: Record<string, unknown>): boolean {\n  return typeof target.memory_session_id === 'string' || typeof target.content_session_id === 'string';\n}\nif (!hasSessionTarget(op.body.target)) throw new Error('set_title target needs memory_session_id or content_session_id');","typeGuard":"const hasSessionId = (t: unknown): t is { memory_session_id?: string; content_session_id?: string } & Record<string, unknown> =>\n  typeof t === 'object' && t !== null && ('memory_session_id' in t || 'content_session_id' in t);","tryCatchPattern":"try { apply(op); } catch (e) {\n  if (e instanceof InvalidOpError && e.reason.includes('memory_session_id or content_session_id')) {\n    // re-resolve the session id and re-emit the op\n  } else throw e;\n}","preventionTips":["Resolve the session id on the producer before building the op","Keep target key names in one shared constants module for producer and consumer","Add an integration test that round-trips set_title ops through the sync log"],"tags":["sync","validation","identifier"],"backgroundTag":"missing-required-argument","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}