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}: unknown row kind What it means
applyRowOp dispatches on the op's row kind (session/observation/summary/prompt). Any other kind string hits the default branch and is rejected. This guards against typos and unknown kinds from newer producers whose kinds the current applier cannot map to a table.
Solutions
- Check op.kind for seq=${op.seq}; fix the producer to use one of the supported kinds (session/observation/summary/prompt as dispatched in applyRowOp).
- Upgrade this node to a build that recognizes the new kind if a newer producer introduced it.
- Hold back ops of unknown kinds until all nodes are upgraded, or drop and re-create them with a supported kind.
- Normalize kind casing on the producer to lowercase exactly as the applier expects.
Example fix
// before
{ kind: 'Summary', ... }
// after
{ kind: 'summary', ... } Defensive patterns
Strategy: validation
Validate before calling
const ROW_KINDS = ['session','observation','summary','prompt'];
if (!ROW_KINDS.includes(op.kind)) throw new Error(`unsupported kind: ${op.kind}`); Type guard
const isKnownRowKind = (k) => ['session','observation','summary','prompt'].includes(k);
Try / catch
try {
applySyncOp(op);
} catch (e) {
if (String(e.message).includes('unknown row kind')) {
logger.warn({ seq: op.seq, kind: op.kind }, 'unknown kind — hold until upgrade');
holdQueue.push(op);
} else throw e;
} Prevention
- Centralize the kind constant list and import it on both producer and applier
- Keep kinds lowercase and exact; validate at enqueue time
- Upgrade all nodes before a producer starts emitting new row kinds
When it happens
Trigger: op.kind is misspelled (e.g. 'obsrvation', 'summaries'), a newer producer version introduced a row kind this build doesn't know, or the kind field was set to null/undefined and stringified oddly.
Common situations: Version skew: a device upgraded to a schema that adds a new row kind syncs to an older node; a hand-written migration tool emitted an ad-hoc kind; a case-sensitivity bug ('Summary' vs 'summary').
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- SyncApply: invalid op seq=
- Backfill failed
- canonical content
- Chroma unavailable before write; leaving documents unsynced
- cloud sync ack conflict for
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/812425646b35a386.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/SyncApply.ts:600
updated_at_epoch=excluded.updated_at_epoch
`).run(
op.entity_id, op.kind, op.origin_device, op.origin_id, op.entity_rev,
op.operation_sha256, op.deleted === true ? 1 : 0, this.now(),
);
return outcome;
}
private applyRowOp(op: SyncOp, chromaJobs: ChromaJob[]): 'applied' | 'stale' {
const body = this.parseBody(op);
switch (op.kind) {
case 'observation':
return this.applyObservation(op, body, chromaJobs);
case 'summary':
return this.applySummary(op, body, chromaJobs);
case 'prompt':
return this.applyPrompt(op, body, chromaJobs);
default:
throw invalidOp(op, `unknown row kind`);
}
}
// ------------------------------------------------------------------------
// Title parking (see SET_TITLE CONVERGENCE in the module header)
// ------------------------------------------------------------------------
private static parkedTitleMemKey(memorySessionId: string): string {
return `parked_title:mem:${memorySessionId}`;
}
private static parkedTitleContentKey(platform: string, contentSessionId: string): string {
return `parked_title:content:${platform}:${contentSessionId}`;
}
private parkTitle(key: string, title: string): void {
// Last parked title for an identity wins (log order — a later set_title
// in the same replay overwrites the earlier one).View on GitHub (pinned to d8bc9755e7)