different-ai/openwork · error
A manual occurrence needs a nonce
Error message
A manual occurrence needs a nonce
What it means
automationOccurrenceIdentity builds a stable occurrenceId/idempotencyKey pair from an automationId plus either a scheduled timestamp or a manual nonce. Manual occurrences (scheduledFor === null) have no natural timestamp to key on, so a caller-supplied nonce is required to deduplicate retries. If scheduledFor is null and no nonce is provided, the identity would collide across unrelated manual runs, so the function refuses to fabricate one.
Source
Thrown at packages/automations/src/contracts.ts:44
for (let index = 0; index < input.length; index += 1) {
const code = input.charCodeAt(index)
left = Math.imul(left ^ code, 0x01000193)
right = Math.imul(right ^ code, 0x85ebca6b)
}
return `${(left >>> 0).toString(16).padStart(8, "0")}${(right >>> 0).toString(16).padStart(8, "0")}`
}
export interface AutomationOccurrenceIdentityInput {
automationId: string
scheduledFor: number | null
nonce?: string
}
export function automationOccurrenceIdentity(input: AutomationOccurrenceIdentityInput): {
occurrenceId: string
idempotencyKey: string
} {
if (input.scheduledFor === null && !input.nonce) throw new Error("A manual occurrence needs a nonce")
const occurrence = input.scheduledFor === null ? `manual:${input.nonce}` : String(input.scheduledFor)
const stable = [input.automationId, occurrence]
.map(encodeURIComponent).join(":")
return {
occurrenceId: `automation-occurrence:${stable}`,
idempotencyKey: `automation:${stable}`,
}
}
View on GitHub (pinned to 2b7df46e8a)
Solutions
- Pass a unique nonce, e.g. crypto.randomUUID(), for every manual occurrence
- If the run is actually scheduled, supply the real scheduledFor timestamp instead of null
- Audit the calling helper (identity/scheduled) to ensure manual paths always populate nonce
Example fix
// before
const id = automationOccurrenceIdentity({ automationId: "a1", scheduledFor: null })
// after
const id = automationOccurrenceIdentity({ automationId: "a1", scheduledFor: null, nonce: crypto.randomUUID() }) Defensive patterns
Strategy: validation
Validate before calling
if (input.scheduledFor === null && !input.nonce) {
throw new Error("Manual occurrence requires a nonce")
}
automationOccurrenceIdentity(input) Type guard
const isManualWithNonce = (i: AutomationOccurrenceIdentityInput): i is AutomationOccurrenceIdentityInput & { nonce: string } =>
i.scheduledFor !== null || (typeof i.nonce === "string" && i.nonce.length > 0) Try / catch
try {
const id = automationOccurrenceIdentity(input)
} catch (e) {
if (e instanceof Error && e.message === "A manual occurrence needs a nonce") {
return automationOccurrenceIdentity({ ...input, nonce: crypto.randomUUID() })
}
throw e
} Prevention
- Always generate a nonce (crypto.randomUUID()) at the manual-trigger entry point
- Centralize manual-run creation in one helper so nonce generation can't be skipped
- Add a unit test asserting manual identity inputs always include a nonce
When it happens
Trigger: Calling automationOccurrenceIdentity({ automationId, scheduledFor: null }) with nonce undefined/empty string. Typically from identity() or a scheduled() helper that resolves a manual trigger without generating a nonce.
Common situations: Manually triggering an automation run via a button/CLI where the caller forgot to generate a nonce (e.g. crypto.randomUUID()); a refactor that changed scheduledFor to null for 'run now' semantics without adding nonce plumbing.
Related errors
- invalid_plugin_payload
- automation_revision_changed
- automation_cloud_result_not_durable
- workflow_test_receipt_already_used
- 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/b73b8674aa548949.
Report an issue: GitHub.