different-ai/openwork · warning
automation_revision_changed
automation_revision_changed
Error message
automation_revision_changed
What it means
DenAutomationRepository.recordSkippedManual locks the automation row FOR UPDATE inside a transaction and verifies that the revision the caller passed still matches the row's current_revision_id. If the automation was edited (or a new revision was published) between the caller reading revisionId and recording the skip, this error aborts the transaction so a stale occurrence is never recorded against a superseded revision.
Source
Thrown at ee/apps/den-api/src/automations/repository.ts:541
organizationId: string
ownerMemberId: string
automation: Automation
revision: AutomationRevision
nonce: string
code: AutomationError["code"]
message: string
now: number
}): Promise<AutomationRun> {
return db.transaction(async (tx) => {
const automationId = normalizeAutomationId(input.automation.id)
const revisionId = normalizeRevisionId(input.revision.id)
const locked = await tx.select().from(AutomationTable).where(and(
eq(AutomationTable.id, automationId),
eq(AutomationTable.organization_id, normalizeOrganizationId(input.organizationId)),
eq(AutomationTable.owner_member_id, normalizeMemberId(input.ownerMemberId)),
)).limit(1).for("update")
if (!locked[0] || locked[0].state === "archived") throw new Error("automation_not_found")
if (locked[0].current_revision_id !== revisionId) throw new Error("automation_revision_changed")
const identity = automationOccurrenceIdentity({
automationId: input.automation.id,
scheduledFor: null,
nonce: input.nonce,
})
const runId = createDenTypeId("automationRun")
await tx.insert(AutomationRunTable).values({
id: runId,
automation_id: automationId,
revision_id: revisionId,
trigger: "manual",
scheduled_for: null,
idempotency_key: identity.idempotencyKey,
status: "skipped",
execution_target: input.revision.executionTarget ?? "desktop",
claim_deadline_at: null,
lease_owner: null,View on GitHub (pinned to 2b7df46e8a)
Solutions
- Re-fetch the automation's current revision via the repository/API and re-issue recordSkippedManual with the current revisionId
- If the occurrence belongs to an old revision intentionally, drop the stale occurrence instead of recording the skip
- Use optimistic concurrency on the caller side: capture both automation id and revision id together right before the call
Example fix
// before
await repo.recordSkippedManual({ ...input, revisionId: staleRevisionId })
// after
const current = await repo.automationById(input.automationId)
await repo.recordSkippedManual({ ...input, revisionId: current.currentRevisionId }) Defensive patterns
Strategy: validation
Validate before calling
const current = await repo.automationById(automationId)
if (current.currentRevisionId !== revisionId) {
throw new Error('stale revision: re-fetch before recordSkippedManual')
} Type guard
function isCurrentRevision(current: { currentRevisionId: string }, revisionId: string): boolean {
return current.currentRevisionId === revisionId
} Try / catch
try {
await repo.recordSkippedManual(input)
} catch (e) {
if (String((e as Error)?.message) === 'automation_revision_changed') {
// refresh revision and retry once, or drop the stale occurrence
} else throw e
} Prevention
- Always load the automation and its current revision atomically with the decision to skip
- Avoid long delays between reading revisionId and calling recordSkippedManual
- Make occurrence payloads carry the revision they belong to and discard stale ones
When it happens
Trigger: Calling recordSkippedManual with a revisionId that no longer equals locked[0].current_revision_id — i.e. the automation was updated/republished after the manual-run occurrence was captured.
Common situations: A user edits the automation while a previously scheduled/manual occurrence is being acknowledged as skipped; a race between an editor publishing a new revision and a worker recording skips for the old revision; retrying an old request after the automation changed.
Related errors
- agent_diagnostics_in_progress
- agent_diagnostics_busy
- automation_run_lease_lost
- automation_cloud_result_not_durable
- connector_mapping_not_found
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/7aa69c69241a1c9b.
Report an issue: GitHub.