{"record":{"id":"bab2b2b4bd56fbb3","repo":"coleam00/Archon","slug":"failed-to-fail-workflow-run-err-message","errorCode":null,"errorMessage":"Failed to fail workflow run: ${err.message}","messagePattern":"Failed to fail workflow run: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/db/workflows.ts","lineNumber":1298,"sourceCode":"            deadline_at: parsedSchedule.deadlineAt,\n            attempt: parsedSchedule.attempt,\n            max_attempts: parsedSchedule.maxAttempts,\n          },\n        });\n      }\n      if ((update.rowCount ?? 0) > 0) {\n        await insertWorkflowEvent(query, {\n          workflow_run_id: id,\n          event_type: 'workflow_failed',\n          data: { error },\n        });\n      }\n      return update;\n    });\n  } catch (dbError) {\n    const err = dbError as Error;\n    getLog().error({ err }, 'db.workflow_run_mark_failed_error');\n    throw new Error(`Failed to fail workflow run: ${err.message}`);\n  }\n  if (result.rowCount === 0) {\n    getLog().warn({ workflowRunId: id }, 'db.workflow_run_fail_no_match');\n    throw new Error(`Workflow run not found or already terminal (id: ${id})`);\n  }\n}\n\nexport async function cancelWorkflowRun(\n  id: string,\n  event?: WorkflowCancellationEventDetails\n): Promise<{ cancelled: boolean }> {\n  const dialect = getDialect();\n  let result: Awaited<ReturnType<IDatabase['query']>>;\n  try {\n    // Guard against re-stamping an already-finished run. Cancelling a run that\n    // is 'completed' or 'cancelled' must be a no-op, not a re-write of\n    // completed_at / a resurrection of terminal state. 'failed' is intentionally\n    // still cancellable (it remains a resumable state, so the user must be able","sourceCodeStart":1280,"sourceCodeEnd":1316,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/db/workflows.ts#L1280-L1316","documentation":"failWorkflowRun() marks a run 'failed' (from 'running' or 'pending') in a transaction and records a workflow_failed event. This wrapper rethrows any database error from that transaction with the original message appended. It is distinct from the 'no matching row' case (error 263).","triggerScenarios":"Calling failWorkflowRun(id, error, scheduledResume?) when the UPDATE or the workflow_failed/quota_resume_scheduled event insert throws: DB connection failure, constraint violation on the event insert, invalid scheduledResume failing scheduledWorkflowResumeSchema.parse (thrown inside the try), or dialect-specific JSON manipulation error.","commonSituations":"Database outage while an executor is trying to record a failure; scheduledResume object missing required fields (resumeAt/deadlineAt/attempt/max_attempts) so schema.parse throws inside the try block; event table schema drift between SQLite and PostgreSQL; transaction rollback from a deadlock.","solutions":["Read the appended underlying message and the 'db.workflow_run_mark_failed_error' log entry","If scheduledResume was passed, validate it against scheduledWorkflowResumeSchema before calling (resumeAt, deadlineAt, attempt, max_attempts)","Verify database connectivity and retry once the DB is healthy (the run must still be non-terminal)","Confirm schema/migrations are current for remote_agent_workflow_runs and the workflow events table","If failing due to an invalid resume schedule, retry without the scheduledResume argument"],"exampleFix":"// before\nawait failWorkflowRun(id, errMsg, resume as ScheduledWorkflowResume);\n// after\nconst parsed = scheduledWorkflowResumeSchema.safeParse(resume);\nawait failWorkflowRun(id, errMsg, parsed.success ? parsed.data : undefined);","handlingStrategy":"try-catch","validationCode":"const run = await getWorkflowRun(id);\nif (!run) throw new Error(`run ${id} does not exist`);\nif (!['running', 'pending'].includes(run.status)) throw new Error(`run ${id} already ${run.status}`);\nif (scheduledResume) scheduledWorkflowResumeSchema.parse(scheduledResume); // throws early with clear message","typeGuard":"function isScheduledResume(v: unknown): v is ScheduledWorkflowResume {\n  return scheduledWorkflowResumeSchema.safeParse(v).success;\n}","tryCatchPattern":"try {\n  await failWorkflowRun(id, message, scheduledResume);\n} catch (err) {\n  getLog().error({ err, runId: id }, 'failWorkflowRun db error');\n  if (isTransientDbError(err)) await backoffThenRetry(() => failWorkflowRun(id, message));\n  else throw err;\n}","preventionTips":["Validate scheduledResume against the schema before calling","Ensure DB connectivity during executor shutdown/cleanup paths","Keep SQLite/PostgreSQL schemas aligned","Check run status is 'running' or 'pending' before failing","Route all failure writes through one owner to reduce transaction contention"],"tags":["database","workflow","state-transition"],"backgroundTag":"workflow-state-update-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}