coleam00/Archon · error

Failed to cancel workflow run: ${err.message}

Error message

Failed to cancel workflow run: ${err.message}

What it means

cancelWorkflowRun() sets status='cancelled' unless the run is already 'completed' or 'cancelled' (a 'failed' run remains cancellable). This wrapper rethrows database errors from the transaction with the original message appended. A zero-row update is NOT an error here — it returns { cancelled: false } as an idempotent no-op.

Source

Thrown at packages/core/src/db/workflows.ts:1340

        `UPDATE remote_agent_workflow_runs
         SET status = 'cancelled', completed_at = ${dialect.now()}
         WHERE id = $1 AND status NOT IN ('completed', 'cancelled')`,
        [id]
      );
      if ((update.rowCount ?? 0) > 0) {
        await insertWorkflowEvent(query, {
          workflow_run_id: id,
          event_type: 'workflow_cancelled',
          step_name: event?.step_name,
          data: event?.reason === undefined ? undefined : { reason: event.reason },
        });
      }
      return update;
    });
  } catch (error) {
    const err = error as Error;
    getLog().error({ err }, 'db.workflow_run_cancel_failed');
    throw new Error(`Failed to cancel workflow run: ${err.message}`);
  }
  const cancelled = (result.rowCount ?? 0) > 0;
  if (!cancelled) {
    // Idempotent no-op: the run was already terminal. Returned so callers can
    // report "nothing to cancel" instead of a false "Cancelled" (see #1830 I1).
    // Same info level as the resume CAS-miss signal for consistency (S2).
    getLog().info({ workflowRunId: id }, 'db.workflow_run_cancel_noop');
  }
  return { cancelled };
}

export async function cancelFanOutRun(
  id: string,
  reason: FanOutCancelReason
): Promise<{ cancelled: boolean }> {
  const dialect = getDialect();
  let result: Awaited<ReturnType<IDatabase['query']>>;
  try {

View on GitHub (pinned to 0773b97458)

Solutions

  1. Check the appended underlying message and the 'db.workflow_run_cancel_failed' log for the root cause
  2. Verify database connectivity, then reissue the cancel — it is safe to retry (terminal rows are guarded, repeat cancels return cancelled:false)
  3. Confirm the database schema matches the binary version (apply migrations)
  4. If event details were passed, ensure step_name/reason are simple strings matching WorkflowCancellationEventDetails
  5. Use the returned { cancelled } flag rather than assuming the run stopped; the executor honors cancellation cooperatively

Example fix

// before
await cancelWorkflowRun(id, { step_name: 'deploy', reason: 42 } as any);
// after
const { cancelled } = await cancelWorkflowRun(id, { step_name: 'deploy', reason: 'user requested' });
if (!cancelled) getLog().info({ runId: id }, 'run already terminal; nothing to cancel');
Defensive patterns

Strategy: try-catch

Validate before calling

const run = await getWorkflowRun(id);
if (!run) throw new Error(`run ${id} does not exist`);
if (['completed', 'cancelled'].includes(run.status)) {
  return { cancelled: false }; // cancel would be a no-op anyway
}

Type guard

function isCancellationEvent(v: unknown): v is WorkflowCancellationEventDetails {
  const e = v as WorkflowCancellationEventDetails;
  return (e.step_name === undefined || typeof e.step_name === 'string') &&
         (e.reason === undefined || typeof e.reason === 'string');
}

Try / catch

try {
  const { cancelled } = await cancelWorkflowRun(id, event);
  if (!cancelled) getLog().info({ runId: id }, 'nothing to cancel');
} catch (err) {
  getLog().error({ err, runId: id }, 'cancel db error');
  await backoffThenRetry(() => cancelWorkflowRun(id)); // retry is safe: terminal rows are guarded
}

Prevention

When it happens

Trigger: Calling cancelWorkflowRun(id, event?) when the UPDATE or the workflow_cancelled event insert throws: DB connection failure, constraint violation on the event insert, dialect JSON/now() failure, or transaction rollback/deadlock.

Common situations: Database unreachable or restarted while an operator or executor issues a cancel; workflow_events table missing a newly added column (schema drift between binary and database); long-running transaction contention on the run row; passing an event detail object with an unexpected shape that breaks the insert.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/5069455cd9db7d9a. Report an issue: GitHub.