different-ai/openwork · error
Automation engine event idempotency key was repeated
Error message
Automation engine event idempotency key was repeated
What it means
accept() tracks every event's idempotencyKey and rejects repeats, guaranteeing that replay of the event stream never double-counts an effect. A duplicate key means the same event (same logical effect) was delivered twice under the same key rather than being deduplicated upstream.
Source
Thrown at packages/automations/src/engine.ts:234
if (!Number.isSafeInteger(afterSequence) || afterSequence < 0) {
throw new Error("Automation engine event cursor must be a non-negative integer")
}
let cursor = afterSequence
const eventKeys = new Set<string>()
return {
get cursor() {
return cursor
},
accept(rawEvent) {
const event = automationEngineEventSchema.parse(rawEvent)
if (event.executionId !== receipt.executionId || event.runId !== receipt.runId) {
throw new Error("Automation engine event receipt mismatch")
}
if (event.sequence !== cursor + 1) {
throw new Error("Automation engine event sequence is not contiguous")
}
if (eventKeys.has(event.idempotencyKey)) {
throw new Error("Automation engine event idempotency key was repeated")
}
eventKeys.add(event.idempotencyKey)
cursor = event.sequence
},
}
}
View on GitHub (pinned to 2b7df46e8a)
Solutions
- Deduplicate events by idempotencyKey in the consumer before calling accept()
- Ensure the producer generates a fresh idempotencyKey per logical event, even on retries
- Track consumed keys (or cursor checkpoints) externally so overlapping replays skip already-accepted events
Example fix
// before
for (const e of events) validator.accept(e)
// after
const seen = new Set()
for (const e of events) { if (seen.has(e.idempotencyKey)) continue; seen.add(e.idempotencyKey); validator.accept(e) } Defensive patterns
Strategy: validation
Validate before calling
const seen = new Set<string>()
for (const e of rawEvents) {
if (seen.has(e.idempotencyKey)) continue
seen.add(e.idempotencyKey)
validator.accept(e)
} Type guard
const isUniqueKey = (e: AutomationEngineEvent, seen: Set<string>): boolean => !seen.has(e.idempotencyKey)
Try / catch
try {
validator.accept(event)
} catch (e) {
if (e instanceof Error && e.message.includes("idempotency key was repeated")) {
logger.warn({ key: event.idempotencyKey }, "duplicate engine event skipped")
} else throw e
} Prevention
- Generate a fresh idempotencyKey per logical event on the producer, stable across delivery retries
- Deduplicate by key at the consumer boundary before acceptance
- Record consumed keys/checkpoints so overlapping replays skip already-accepted events
When it happens
Trigger: Calling accept() twice with events carrying the same idempotencyKey — e.g. the producer re-emitted an event without a new key, or the consumer re-fed an already-accepted event (sequence check passed because cursor advanced elsewhere).
Common situations: At-least-once delivery from a queue replaying messages; a producer bug reusing keys across retries; manually replaying an event log slice that overlaps a previously consumed range.
Related errors
- invalid_plugin_payload
- workflow_test_receipt_already_used
- A manual occurrence needs a nonce
- Automation engine event receipt mismatch
- Automation engine event sequence is not contiguous
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/aea3528b97e06135.
Report an issue: GitHub.