thedotmack/claude-mem · error · Error
SyncApply: invalid op seq=
Error message
SyncApply: invalid op seq=${op.seq} kind=${op.kind} origin=${op.origin_device}/${op.origin_id}: remap_project requires where and fields objects What it means
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.
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
Example fix
// before
body: { fields: 'project=proj-b' }
// after
body: { where: { project: 'proj-a' }, fields: { project: 'proj-b' } } Defensive patterns
Strategy: validation
Validate before calling
function isValidRemapBody(body: unknown): boolean {
if (!body || typeof body !== 'object') return false;
const b = body as Record<string, unknown>;
const isObj = (v: unknown) => !!v && typeof v === 'object' && !Array.isArray(v);
return isObj(b.where) && isObj(b.fields);
}
if (!isValidRemapBody(op.body)) throw new Error('remap_project needs where and fields objects'); Type guard
const isPlainObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
Try / catch
try { apply(op); } catch (e) {
if (e instanceof InvalidOpError && e.reason.includes('remap_project requires where and fields')) {
quarantineOp(e.seq);
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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').
Common situations: 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.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- canonical content
- device_id must be 1-128 characters
- deviceId must be non-empty
- name must be 1-80 characters
- ops[ ]
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/64a35dabb33e03b5.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/SyncApply.ts:1085
memorySessionId,
(fields && fieldString(op, fields, 'project')) ?? 'unknown',
op.server_ts,
fields ? fieldString(op, fields, 'content_session_id') : null,
fields ? fieldString(op, fields, 'platform_source') : null
);
// synced_at deliberately untouched: the repair travels through the log
// itself, so applying it must not queue a re-push (echo guard).
this.db.prepare(
'UPDATE user_prompts SET session_db_id = ?, sync_rev = ? WHERE id = ?'
).run(sessionDbId, op.rev, row.id);
return 'applied';
}
private applyRemapProject(op: SyncOp, body: Record<string, unknown>): 'applied' | 'stale' {
const where = body.where as Record<string, unknown> | undefined;
const fields = body.fields as Record<string, unknown> | undefined;
if (!where || typeof where !== 'object' || !fields || typeof fields !== 'object') {
throw invalidOp(op, 'remap_project requires where and fields objects');
}
const setClauses: string[] = [];
const setParams: (string | number)[] = [];
const newProject = fieldString(op, fields, 'project');
const mergedInto = fieldString(op, fields, 'merged_into_project');
if (newProject !== null) {
setClauses.push('project = ?');
setParams.push(newProject);
}
if (mergedInto !== null) {
setClauses.push('merged_into_project = ?');
setParams.push(mergedInto);
}
if (setClauses.length === 0) {
throw invalidOp(op, 'remap_project fields must set project and/or merged_into_project');
}
View on GitHub (pinned to d8bc9755e7)