different-ai/openwork · error
Automation engine event cursor must be a non-negative intege
Error message
Automation engine event cursor must be a non-negative integer
What it means
createAutomationEngineEventSequenceValidator replays engine events starting from a cursor (afterSequence). The cursor must be a non-negative safe integer because it is compared against event.sequence to enforce contiguity. Passing a negative, fractional, or non-integer value (including NaN, Infinity, or a string parsed from persistence) is rejected up front.
Source
Thrown at packages/automations/src/engine.ts:217
): Promise<AutomationEngineReadResult | null>
cancel(
receipt: AutomationEngineAdmissionReceipt,
): Promise<AutomationEngineCancellationResult>
}
export interface AutomationEngineEventSequenceValidator {
readonly cursor: number
accept(event: AutomationEngineEvent): void
}
/** 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")
}View on GitHub (pinned to 2b7df46e8a)
Solutions
- Default the cursor to 0 when no checkpoint exists instead of using -1 or undefined
- Coerce persisted values with Number() and validate Number.isSafeInteger(v) && v >= 0 before calling
- Pass no second argument to restart validation from the beginning (default 0)
Example fix
// before const v = createAutomationEngineEventSequenceValidator(receipt, checkpoint.cursor) // -1 // after const start = Number.isSafeInteger(checkpoint?.cursor) && checkpoint.cursor >= 0 ? checkpoint.cursor : 0 const v = createAutomationEngineEventSequenceValidator(receipt, start)
Defensive patterns
Strategy: validation
Validate before calling
function safeCursor(v: unknown): number {
const n = typeof v === "string" ? Number(v) : v
return typeof n === "number" && Number.isSafeInteger(n) && n >= 0 ? n : 0
}
createAutomationEngineEventSequenceValidator(receipt, safeCursor(checkpoint?.cursor)) Type guard
const isValidCursor = (v: unknown): v is number => typeof v === "number" && Number.isSafeInteger(v) && v >= 0
Try / catch
try {
validator = createAutomationEngineEventSequenceValidator(receipt, cursor)
} catch (e) {
if (e instanceof Error && e.message.includes("non-negative integer")) {
validator = createAutomationEngineEventSequenceValidator(receipt, 0)
} else throw e
} Prevention
- Never use -1 as a 'no checkpoint' sentinel; use null/undefined and default to 0
- Validate every persisted cursor with Number.isSafeInteger before resuming
- Type the checkpoint field as number at the persistence boundary and coerce strings there
When it happens
Trigger: Calling createAutomationEngineEventSequenceValidator(receipt, afterSequence) where afterSequence is negative, not a safe integer (e.g. 1.5, NaN, Infinity), or was loaded from storage as a string/undefined-coerced value.
Common situations: Persisting the cursor in a JSON store and reading it back as a string; subtracting with undefined fields; a stale checkpoint row holding -1 as a sentinel for 'no cursor'.
Related errors
- Agent context diagnostics timeout must be between 1 ms and 3
- Invalid cloud provider sync response.
- Invalid cloud provider sync status.
- Invalid cloud provider sync status response.
- t("providers.provider_id_required")
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/c6729b51e63a69b1.
Report an issue: GitHub.