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

  1. Deduplicate events by idempotencyKey in the consumer before calling accept()
  2. Ensure the producer generates a fresh idempotencyKey per logical event, even on retries
  3. 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

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


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/aea3528b97e06135. Report an issue: GitHub.