{"record":{"id":"3f7ff5446dfdb49d","repo":"different-ai/openwork","slug":"automation-cloud-result-not-durable","errorCode":"automation_cloud_result_not_durable","errorMessage":"automation_cloud_result_not_durable","messagePattern":"automation_cloud_result_not_durable","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-api/src/automations/repository.ts","lineNumber":718,"sourceCode":"      usage: input.usage ?? emptyUsage,\n      error: input.error,\n      now: input.now,\n    })\n    await db.update(AutomationRunTable).set({\n      codemode_receipt_id: input.codemodeReceiptId ? normalizeDenTypeId(\"workflowRun\", input.codemodeReceiptId) : null,\n      validated_result: input.validatedResult,\n      updated_at: new Date(input.now),\n    }).where(eq(AutomationRunTable.id, normalizeRunId(input.runId)))\n    const artifactState = cloudArtifactStateUpdate({\n      runId: input.runId,\n      result: input.status === \"succeeded\"\n        ? { ok: true, value: input.validatedResult }\n        : { ok: false, message: input.error?.message ?? input.resultSummary },\n      now: input.now,\n    })\n    if (input.updateArtifactState === false) {\n      const refreshed = await this.runById(completed.id)\n      if (!refreshed) throw new Error(\"automation_cloud_result_not_durable\")\n      return refreshed\n    }\n    if (artifactState.kind === \"succeeded\") {\n      await db.update(AutomationTable).set({\n        latest_successful_run_id: normalizeRunId(artifactState.latestSuccessfulRunId),\n        latest_successful_result: artifactState.latestSuccessfulResult,\n        state: artifactState.state,\n        needs_attention_reason: artifactState.needsAttentionReason,\n        updated_at: new Date(input.now),\n      }).where(eq(AutomationTable.id, normalizeAutomationId(input.automationId)))\n    } else {\n      await db.update(AutomationTable).set({\n        state: artifactState.state,\n        needs_attention_reason: artifactState.needsAttentionReason,\n        updated_at: new Date(input.now),\n      }).where(eq(AutomationTable.id, normalizeAutomationId(input.automationId)))\n    }\n    const refreshed = await this.runById(completed.id)","sourceCodeStart":700,"sourceCodeEnd":736,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/automations/repository.ts#L700-L736","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm runById returns the completed run directly — investigate why read-after-write fails (read replicas, RLS, connection routing)","Check that the runId used for completion and read-back is identical after normalization","Look for concurrent jobs/cron cleanup that may delete automation_runs rows immediately","Retry completeCloud once; if persistent, treat the run record as lost and alert rather than silently returning"],"exampleFix":"// before\nawait repo.completeCloud({ runId, updateArtifactState: false, ... })\n// after\ntry {\n  await repo.completeCloud({ runId, updateArtifactState: false, ... })\n} catch (e) {\n  if (String(e?.message).includes('not_durable')) {\n    const run = await repo.runById(runId)\n    console.error(`completed run ${runId} not readable afterwards:`, !!run)\n  }\n  throw e\n}","handlingStrategy":"try-catch","validationCode":"const check = await repo.runById(runId)\nif (!check) throw new Error(`run ${runId} not readable before completion`)","typeGuard":"function runExists(run: { id: string } | null | undefined): run is { id: string } {\n  return typeof run === 'object' && run !== null && typeof run.id === 'string'\n}","tryCatchPattern":"try {\n  await repo.completeCloud({ ...input, updateArtifactState: false })\n} catch (e) {\n  if (String((e as Error)?.message) === 'automation_cloud_result_not_durable') {\n    // investigate read-after-write failure; do not silently swallow\n  } else throw e\n}","preventionTips":["Avoid updateArtifactState:false unless you have a reason; let the repository manage artifact state","Ensure runById and the transaction share the same DB connection/read concern (no replica lag)","Check for cleanup jobs that could delete runs concurrently with completion"],"tags":["database","persistence","automations"],"backgroundTag":"write-not-durable","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}