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}: prompt body requires content_session_id, prompt_number, prompt_text, created_at_epoch What it means
applyPrompt requires content_session_id, prompt_number, prompt_text, and created_at_epoch. user_prompts rows are keyed by their session and prompt ordinal, so a prompt without these cannot be deduplicated, ordered, or linked to its conversation — the op is rejected.
Solutions
- Fix the producer to include all four fields: contentSessionId (string), promptNumber (number), promptText (non-empty string), createdAtEpoch (number).
- Skip enqueueing prompts with empty prompt_text on the producer instead of sending an empty-string placeholder.
- Resolve session attribution before capture: buffer prompts until content_session_id is known.
- Validate the payload pre-enqueue to catch missing keys before the op reaches the sync log.
Example fix
// before
body: { prompt_text: ' ' }
// after
body: { prompt_text: text.trim(), content_session_id: sessionId, prompt_number: n, created_at_epoch: Date.now() } Defensive patterns
Strategy: validation
Validate before calling
function isValidPromptBody(b) {
return typeof b.content_session_id === 'string' && b.content_session_id.length > 0
&& typeof b.prompt_number === 'number' && Number.isFinite(b.prompt_number)
&& typeof b.prompt_text === 'string' && b.prompt_text.length > 0
&& typeof b.created_at_epoch === 'number' && Number.isFinite(b.created_at_epoch);
} Type guard
function hasRequiredPromptFields(b) {
return b != null && 'content_session_id' in b && 'prompt_number' in b && 'prompt_text' in b && 'created_at_epoch' in b;
} Try / catch
try {
applySyncOp(op);
} catch (e) {
if (String(e.message).includes('prompt body requires')) {
deadLetterQueue.push(op); // fix session attribution / trim text and re-emit
} else throw e;
} Prevention
- Resolve content_session_id before capturing a prompt; buffer until known
- Drop empty/whitespace-only prompts at capture time
- Validate prompt payloads against a schema before enqueueing
When it happens
Trigger: A prompt op body omits content_session_id, has prompt_number null or non-numeric, has empty/null prompt_text, or lacks created_at_epoch.
Common situations: A capture hook fired for a prompt before session attribution was resolved; prompt_number was lost when parsing a transcript; prompt_text was empty (e.g. only an attachment was sent); an exporter stripped whitespace-only prompts.
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
- SyncApply: invalid op seq=
- SyncApply: invalid op seq=
- canonical content
- device_id must be 1-128 characters
- deviceId must be non-empty
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/492045ed5ca662ec.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/SyncApply.ts:893
// it once the origin registers).
const contentSessionId = fieldString(op, body, 'content_session_id');
if (!contentSessionId) return null;
const platform = normalizePlatformSource(fieldString(op, body, 'platform_source') ?? undefined);
const row = this.db.prepare(`
SELECT id FROM sdk_sessions
WHERE COALESCE(NULLIF(platform_source, ''), ?) = ? AND content_session_id = ?
LIMIT 1
`).get(DEFAULT_PLATFORM_SOURCE, platform, contentSessionId) as { id: number } | undefined;
return row?.id ?? null;
}
private applyPrompt(op: SyncOp, body: Record<string, unknown>, chromaJobs: ChromaJob[]): 'applied' | 'stale' {
const contentSessionId = fieldString(op, body, 'content_session_id');
const promptNumber = fieldNumber(op, body, 'prompt_number');
const promptText = fieldString(op, body, 'prompt_text');
const createdAtEpoch = fieldNumber(op, body, 'created_at_epoch');
if (!contentSessionId || promptNumber === null || promptText === null || createdAtEpoch === null) {
throw invalidOp(op, 'prompt body requires content_session_id, prompt_number, prompt_text, created_at_epoch');
}
const createdAt = fieldString(op, body, 'created_at') ?? new Date(createdAtEpoch).toISOString();
const existing = this.findByOrigin('user_prompts', op.origin_device, op.origin_id);
if (existing) {
if (compareCanonicalDecimals(op.rev, existing.sync_rev) <= 0) return 'stale';
const sessionDbId = this.resolvePromptSession(op, body, createdAtEpoch);
this.db.prepare(`
UPDATE user_prompts SET
session_db_id = ?, content_session_id = ?, prompt_number = ?, prompt_text = ?,
created_at = ?, created_at_epoch = ?, sync_rev = ?, synced_at = ?
WHERE id = ?
`).run(
sessionDbId, contentSessionId, promptNumber, promptText,
createdAt, createdAtEpoch, op.rev, this.now(),
existing.id
);
return 'applied';View on GitHub (pinned to d8bc9755e7)