{"record":{"id":"94bad36b9d423446","repo":"coleam00/Archon","slug":"failed-to-claim-write-back-apply-err-message","errorCode":null,"errorMessage":"Failed to claim write-back apply: ${err.message}","messagePattern":"Failed to claim write-back apply: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/db/workflows.ts","lineNumber":1731,"sourceCode":" */\nexport async function claimWriteback(id: string): Promise<{ claimed: boolean }> {\n  const dialect = getDialect();\n  const extract =\n    getDatabaseType() === 'postgresql'\n      ? \"metadata->>'writeback_apply_claimed'\"\n      : \"json_extract(metadata, '$.writeback_apply_claimed')\";\n  try {\n    const result = await pool.query(\n      `UPDATE remote_agent_workflow_runs\n       SET metadata = ${dialect.jsonMerge('metadata', 2)}\n       WHERE id = $1 AND (${extract} IS NULL)`,\n      [id, JSON.stringify({ writeback_apply_claimed: true })]\n    );\n    return { claimed: (result.rowCount ?? 0) > 0 };\n  } catch (error) {\n    const err = error as Error;\n    getLog().error({ err, workflowRunId: id }, 'db.workflow_run_claim_writeback_failed');\n    throw new Error(`Failed to claim write-back apply: ${err.message}`);\n  }\n}\n\n/**\n * Release a previously-claimed write-back apply (R2-F4) after the apply FAILED, so a\n * subsequent `workflow resume` can re-claim and retry. Explicit-null so SQLite's\n * json_patch removes the key (Postgres `||` sets JSON null); `claimWriteback`'s\n * `IS NULL` check treats both as unclaimed. Best-effort — a failure here leaves the\n * claim set (the volume is preserved regardless; the operator reconciles manually).\n */\nexport async function releaseWritebackClaim(id: string): Promise<void> {\n  const dialect = getDialect();\n  await pool.query(\n    `UPDATE remote_agent_workflow_runs\n     SET metadata = ${dialect.jsonMerge('metadata', 2)}\n     WHERE id = $1`,\n    [id, JSON.stringify({ writeback_apply_claimed: null })]\n  );","sourceCodeStart":1713,"sourceCodeEnd":1749,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/db/workflows.ts#L1713-L1749","documentation":"Thrown by the write-back apply claim function when the conditional UPDATE that sets `metadata.writeback_apply_claimed = true` fails. This claim (R2-F4) must be acquired before mutating the live root; a driver/SQL failure is logged as `db.workflow_run_claim_writeback_failed` and rethrown with this message. `{claimed:false}` means another process already claimed it or the run no longer matches — that is a normal outcome, not this error.","triggerScenarios":"Calling the claim before applying a container write-back when the DB is unreachable, the transaction/UPDATE errors, the metadata column rejects the JSON merge, or lock contention kills the statement.","commonSituations":"Two concurrent resume processes contending on the same run row; database connection drop mid-apply pipeline; metadata JSON corrupted so the merge update fails.","solutions":["Read the `db.workflow_run_claim_writeback_failed` log for the driver-level cause","Retry the claim — losing a race returns claimed:false and a transient DB failure can be retried safely","Verify metadata JSON integrity and dialect-specific update SQL if merge errors appear","Check connection pool health before re-running the write-back apply flow"],"exampleFix":"// before\nawait claimWritebackApply(runId); // throws on DB error\n// after\nlet claim;\ntry {\n  claim = await claimWritebackApply(runId);\n} catch (e) {\n  throw new Error(`write-back apply aborted: claim step failed: ${String(e)}`);\n}\nif (!claim.claimed) return; // already claimed elsewhere — skip apply","handlingStrategy":"try-catch","validationCode":"// check the run is still eligible to claim before apply\nconst run = await getWorkflowRun(id);\nconst claimable = run != null && !run.metadata?.writeback_apply_claimed;","typeGuard":"function isUnclaimed(run: WorkflowRun | undefined): boolean {\n  return run != null && run.metadata?.writeback_apply_claimed !== true;\n}","tryCatchPattern":"let claimed: boolean;\ntry {\n  claimed = (await claimWritebackApply(id)).claimed;\n} catch (error) {\n  log.error({ id, err: error }, 'claim failed; aborting write-back apply');\n  throw error;\n}\nif (!claimed) return; // someone else owns the apply","preventionTips":["Always claim before mutating the live root (R2-F4); never skip the claim","Release the claim on apply failure via the release API so retries can re-claim","Keep apply+claim flows short-lived to reduce lock windows","Alert on `db.workflow_run_claim_writeback_failed` — it blocks the apply pipeline"],"tags":["database","workflow","concurrency"],"backgroundTag":"database-query-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}