different-ai/openwork · error
Automation engine event receipt mismatch
Error message
Automation engine event receipt mismatch
What it means
The sequence validator is bound to a specific admission receipt (executionId + runId). Every event fed to accept() must belong to that same execution/run. If the event's executionId or runId differs from the receipt's, the event stream and the validated admission are out of sync, so the event is rejected.
Source
Thrown at packages/automations/src/engine.ts:228
/** Validates events before Den persists them and advances its durable cursor. */
export function createAutomationEngineEventSequenceValidator(
rawReceipt: AutomationEngineAdmissionReceipt,
afterSequence = 0,
): AutomationEngineEventSequenceValidator {
const receipt = automationEngineAdmissionReceiptSchema.parse(rawReceipt)
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
- Create a fresh validator per execution/run, matching the receipt you obtained from admission
- Filter the event stream by the receipt's executionId and runId before calling accept()
- If the run was re-admitted, re-fetch the new receipt and rebuild the validator
Example fix
// before validator.accept(eventFromOtherExecution) // after if (event.executionId === receipt.executionId && event.runId === receipt.runId) validator.accept(event)
Defensive patterns
Strategy: type-guard
Validate before calling
const belongsToReceipt = (event: AutomationEngineEvent, receipt: AutomationEngineAdmissionReceipt): boolean => event.executionId === receipt.executionId && event.runId === receipt.runId // filter stream before accepting: events.filter(e => belongsToReceipt(e, receipt)).forEach(e => validator.accept(e))
Type guard
const isEventForReceipt = (e: AutomationEngineEvent, r: AutomationEngineAdmissionReceipt): boolean => e.executionId === r.executionId && e.runId === r.runId
Try / catch
try {
validator.accept(event)
} catch (e) {
if (e instanceof Error && e.message === "Automation engine event receipt mismatch") {
// rebuild validator from the receipt matching this event's execution/run
validator = createAutomationEngineEventSequenceValidator(receiptFor(event), event.sequence - 1)
} else throw e
} Prevention
- Scope each validator to one execution/run and never reuse it across runs
- Key event subscriptions by executionId+runId so streams can't interleave
- On re-admission, always discard the old validator and receipt together
When it happens
Trigger: Calling validator.accept(event) with an event produced by a different execution or run than the receipt passed to createAutomationEngineEventSequenceValidator — e.g. mixing event streams, replaying events after a new run started, or a stale validator reused across runs.
Common situations: Caching a validator instance and feeding it a resumed run's events; two concurrent executions writing to a shared event log consumed by one validator; retrying with a new executionId but the old receipt.
Related errors
- Automation engine event sequence is not contiguous
- automation_revision_changed
- automation_cloud_result_not_durable
- A manual occurrence needs a nonce
- Automation engine event cursor must be a non-negative intege
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/bc7efad251acd323.
Report an issue: GitHub.