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

  1. 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).
  2. Upgrade this node to a build that recognizes the new kind if a newer producer introduced it.
  3. Hold back ops of unknown kinds until all nodes are upgraded, or drop and re-create them with a supported kind.
  4. 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

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


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)