different-ai/openwork · error
Automation engine event sequence is not contiguous
Error message
Automation engine event sequence is not contiguous
What it means
accept() enforces a strictly contiguous sequence: each accepted event's sequence must equal the current cursor + 1. This detects lost or reordered events during replay. A gap (skipped number) means events were dropped between the last accepted event and this one.
Source
Thrown at packages/automations/src/engine.ts:231
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
- Replay the event stream from the correct checkpoint (afterSequence matching the last accepted event)
- Ensure the event source preserves ordering and delivers every event exactly once
- If a gap is expected (trimmed log), rebuild the validator from the earliest available sequence instead of mid-stream
Example fix
// before validator.accept(nextEvent) // sequence 5 while cursor is 3 // after if (nextEvent.sequence === validator.cursor + 1) validator.accept(nextEvent) else resumeStreamFrom(validator.cursor)
Defensive patterns
Strategy: validation
Validate before calling
if (event.sequence !== validator.cursor + 1) {
// resync from the source instead of accepting out-of-order
restartReplayFrom(validator.cursor)
} else {
validator.accept(event)
} Type guard
const isNextInSequence = (e: AutomationEngineEvent, cursor: number): boolean => e.sequence === cursor + 1
Try / catch
try {
validator.accept(event)
} catch (e) {
if (e instanceof Error && e.message.includes("not contiguous")) {
await replayFromSource(validator.cursor) // refill missing events then continue
} else throw e
} Prevention
- Consume events from an ordered, lossless source (single-partition log) not an unordered set
- Persist the cursor checkpoint atomically with event processing
- Add an integration test that replays a full stream and asserts no gaps
When it happens
Trigger: Feeding accept() an event whose sequence !== cursor + 1 — skipping a number (event lost), replaying the same sequence twice, or starting the stream at sequence > afterSequence + 1.
Common situations: Reading events from a log that compacts/trims old entries; concurrent writers with gaps; resuming with the wrong afterSequence checkpoint; events published to a lossy transport.
Related errors
- Automation engine event receipt mismatch
- 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/91d53bfc177e448b.
Report an issue: GitHub.