different-ai/openwork · error

automation_cloud_result_not_durable

automation_cloud_result_not_durable

Error message

automation_cloud_result_not_durable

What it means

When completeCloud finishes a run with updateArtifactState === false it skips the artifact-state update and instead re-reads the run via runById; if that read returns nothing the repository cannot prove the just-completed run is durable and throws automation_cloud_result_not_durable. It is a read-back assertion guarding the completion path.

Source

Thrown at ee/apps/den-api/src/automations/repository.ts:718

      usage: input.usage ?? emptyUsage,
      error: input.error,
      now: input.now,
    })
    await db.update(AutomationRunTable).set({
      codemode_receipt_id: input.codemodeReceiptId ? normalizeDenTypeId("workflowRun", input.codemodeReceiptId) : null,
      validated_result: input.validatedResult,
      updated_at: new Date(input.now),
    }).where(eq(AutomationRunTable.id, normalizeRunId(input.runId)))
    const artifactState = cloudArtifactStateUpdate({
      runId: input.runId,
      result: input.status === "succeeded"
        ? { ok: true, value: input.validatedResult }
        : { ok: false, message: input.error?.message ?? input.resultSummary },
      now: input.now,
    })
    if (input.updateArtifactState === false) {
      const refreshed = await this.runById(completed.id)
      if (!refreshed) throw new Error("automation_cloud_result_not_durable")
      return refreshed
    }
    if (artifactState.kind === "succeeded") {
      await db.update(AutomationTable).set({
        latest_successful_run_id: normalizeRunId(artifactState.latestSuccessfulRunId),
        latest_successful_result: artifactState.latestSuccessfulResult,
        state: artifactState.state,
        needs_attention_reason: artifactState.needsAttentionReason,
        updated_at: new Date(input.now),
      }).where(eq(AutomationTable.id, normalizeAutomationId(input.automationId)))
    } else {
      await db.update(AutomationTable).set({
        state: artifactState.state,
        needs_attention_reason: artifactState.needsAttentionReason,
        updated_at: new Date(input.now),
      }).where(eq(AutomationTable.id, normalizeAutomationId(input.automationId)))
    }
    const refreshed = await this.runById(completed.id)

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Confirm runById returns the completed run directly — investigate why read-after-write fails (read replicas, RLS, connection routing)
  2. Check that the runId used for completion and read-back is identical after normalization
  3. Look for concurrent jobs/cron cleanup that may delete automation_runs rows immediately
  4. Retry completeCloud once; if persistent, treat the run record as lost and alert rather than silently returning

Example fix

// before
await repo.completeCloud({ runId, updateArtifactState: false, ... })
// after
try {
  await repo.completeCloud({ runId, updateArtifactState: false, ... })
} catch (e) {
  if (String(e?.message).includes('not_durable')) {
    const run = await repo.runById(runId)
    console.error(`completed run ${runId} not readable afterwards:`, !!run)
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

const check = await repo.runById(runId)
if (!check) throw new Error(`run ${runId} not readable before completion`)

Type guard

function runExists(run: { id: string } | null | undefined): run is { id: string } {
  return typeof run === 'object' && run !== null && typeof run.id === 'string'
}

Try / catch

try {
  await repo.completeCloud({ ...input, updateArtifactState: false })
} catch (e) {
  if (String((e as Error)?.message) === 'automation_cloud_result_not_durable') {
    // investigate read-after-write failure; do not silently swallow
  } else throw e
}

Prevention

When it happens

Trigger: Calling completeCloud with updateArtifactState:false and then runById(completed.id) returns undefined — the run row vanished or the id normalization diverged between the insert path and the lookup.

Common situations: Misconfigured database hiding the freshly written row (replica reads, RLS); run id transformed inconsistently by normalizeRunId; concurrent delete of the run row between completion and read-back.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/3f7ff5446dfdb49d. Report an issue: GitHub.