{"record":{"id":"64a35dabb33e03b5","repo":"thedotmack/claude-mem","slug":"syncapply-invalid-op-seq-op-seq-kind-op-kind-origin-op-64a35d","errorCode":null,"errorMessage":"SyncApply: invalid op seq=${op.seq} kind=${op.kind} origin=${op.origin_device}/${op.origin_id}: remap_project requires where and fields objects","messagePattern":"SyncApply: invalid op seq=(.+?) kind=(.+?) origin=(.+?)/(.+?): remap_project requires where and fields objects","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/sync/SyncApply.ts","lineNumber":1085,"sourceCode":"      memorySessionId,\n      (fields && fieldString(op, fields, 'project')) ?? 'unknown',\n      op.server_ts,\n      fields ? fieldString(op, fields, 'content_session_id') : null,\n      fields ? fieldString(op, fields, 'platform_source') : null\n    );\n    // synced_at deliberately untouched: the repair travels through the log\n    // itself, so applying it must not queue a re-push (echo guard).\n    this.db.prepare(\n      'UPDATE user_prompts SET session_db_id = ?, sync_rev = ? WHERE id = ?'\n    ).run(sessionDbId, op.rev, row.id);\n    return 'applied';\n  }\n\n  private applyRemapProject(op: SyncOp, body: Record<string, unknown>): 'applied' | 'stale' {\n    const where = body.where as Record<string, unknown> | undefined;\n    const fields = body.fields as Record<string, unknown> | undefined;\n    if (!where || typeof where !== 'object' || !fields || typeof fields !== 'object') {\n      throw invalidOp(op, 'remap_project requires where and fields objects');\n    }\n\n    const setClauses: string[] = [];\n    const setParams: (string | number)[] = [];\n    const newProject = fieldString(op, fields, 'project');\n    const mergedInto = fieldString(op, fields, 'merged_into_project');\n    if (newProject !== null) {\n      setClauses.push('project = ?');\n      setParams.push(newProject);\n    }\n    if (mergedInto !== null) {\n      setClauses.push('merged_into_project = ?');\n      setParams.push(mergedInto);\n    }\n    if (setClauses.length === 0) {\n      throw invalidOp(op, 'remap_project fields must set project and/or merged_into_project');\n    }\n","sourceCodeStart":1067,"sourceCodeEnd":1103,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/SyncApply.ts#L1067-L1103","documentation":"applyRemapProject performs a targeted UPDATE of project/merged_into_project columns. It requires both body.where (the row selector) and body.fields (the column assignments) to be plain objects; either being absent or a non-object makes the op meaningless and it is rejected via invalidOp before any SQL is built.","triggerScenarios":"A remap_project op arrives with body.where or body.fields missing, null, an array, or a primitive (e.g. fields sent as a flat string 'project=newproj').","commonSituations":"A producer serializing the remap payload incorrectly; an op built by an older tool version with a different body layout; manual log editing or test fixtures with an abbreviated shape.","solutions":["Emit body.where and body.fields as plain objects in every remap_project op","Fix producer serialization so fields is an object mapping column names to values","Validate the op body on the producer before appending to the sync log"],"exampleFix":"// before\nbody: { fields: 'project=proj-b' }\n// after\nbody: { where: { project: 'proj-a' }, fields: { project: 'proj-b' } }","handlingStrategy":"validation","validationCode":"function isValidRemapBody(body: unknown): boolean {\n  if (!body || typeof body !== 'object') return false;\n  const b = body as Record<string, unknown>;\n  const isObj = (v: unknown) => !!v && typeof v === 'object' && !Array.isArray(v);\n  return isObj(b.where) && isObj(b.fields);\n}\nif (!isValidRemapBody(op.body)) throw new Error('remap_project needs where and fields objects');","typeGuard":"const isPlainObject = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":"try { apply(op); } catch (e) {\n  if (e instanceof InvalidOpError && e.reason.includes('remap_project requires where and fields')) {\n    quarantineOp(e.seq);\n  } else throw e;\n}","preventionTips":["Serialize remap payloads as objects, never flattened strings","Validate the op body on the producer before appending to the log","Add fixtures covering every remap_project field combination"],"tags":["sync","validation","remap"],"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"}