coleam00/Archon · error

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

Error message

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

What it means

completeWorkflowRun() marks a workflow run row as 'completed' inside a transaction (and inserts a workflow_completed event). This wrapper error rethrows any failure from that transaction — connection loss, constraint violation, dialect/JSON merge error, transaction abort — with the original DB message appended. It distinguishes a database-level failure from the 'no matching running row' case (error 261).

Source

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

        : await query(
            `UPDATE remote_agent_workflow_runs
             SET status = 'completed', completed_at = ${dialect.now()}
             WHERE id = $1 AND status = 'running'`,
            [id]
          );
      if ((update.rowCount ?? 0) > 0) {
        await insertWorkflowEvent(query, {
          workflow_run_id: id,
          event_type: 'workflow_completed',
          data: completion,
        });
      }
      return update;
    });
  } catch (error) {
    const err = error as Error;
    getLog().error({ err }, 'db.workflow_run_complete_failed');
    throw new Error(`Failed to complete workflow run: ${err.message}`);
  }
  if (result.rowCount === 0) {
    getLog().warn({ workflowRunId: id }, 'db.workflow_run_complete_no_match');
    throw new Error(`Workflow run not found or not in running state (id: ${id})`);
  }
}

/**
 * Mark a run failed.
 *
 * Matches `pending` as well as `running`. A run can fail BEFORE it ever transitions to
 * running — source capture, artifact setup, and credential resolution all happen against
 * a freshly inserted `pending` row — and a `running`-only guard left those rows pending
 * forever: no terminal state, no error recorded, and nothing to tell the operator the run
 * is dead. Both are non-terminal states owned by this process, so failing either is the
 * same decision. Terminal rows still never transition.
 */
export async function failWorkflowRun(

View on GitHub (pinned to 0773b97458)

Solutions

  1. Inspect the appended original message (err.message) and the 'db.workflow_run_complete_failed' log for the underlying DB error
  2. Verify database connectivity and that the DB server was up for the duration of the run
  3. Ensure the metadata argument is JSON-serializable and matches the expected Record<string, unknown> shape
  4. Confirm the schema is up to date (migrations applied; both SQLite and PostgreSQL shapes aligned)
  5. Retry the completion write once the database is healthy; the run must still be in 'running' state for the update to apply

Example fix

// before
await completeWorkflowRun(id, { duration_ms: 1234 }, metadata as any);
// after
try {
  await completeWorkflowRun(id, { duration_ms: 1234 }, metadata);
} catch (err) {
  if (!err.message.startsWith('Workflow run not found')) {
    getLog().error({ err, workflowRunId: id }, 'complete retryable db failure');
    await completeWorkflowRun(id, { duration_ms: 1234 }); // retry without metadata
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

const run = await getWorkflowRun(id);
if (!run) throw new Error(`run ${id} does not exist`);
if (run.status !== 'running') throw new Error(`run ${id} is ${run.status}, not running`);
JSON.stringify(metadata ?? {}); // ensure metadata is serializable

Type guard

function isRecord(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Try / catch

try {
  await completeWorkflowRun(id, { duration_ms }, metadata);
} catch (err) {
  if (err.message.startsWith('Workflow run not found')) {
    // state issue: reconcile with current run status
  } else {
    // DB failure: log err.message (original cause) and retry after health check
  }
}

Prevention

When it happens

Trigger: Calling completeWorkflowRun(id, completion, metadata?) when the underlying UPDATE/INSERT transaction fails: database unreachable or dropped mid-transaction, metadata JSON serialization/dialect jsonMerge failure, workflow event insert constraint violation, or transaction deadlock/rollback.

Common situations: Postgres/SQLite restarted or connection pooled out during a long workflow; passing metadata containing values that fail JSON.stringify or violate a schema; a duplicate-key error on the workflow event insert; running against a stale database whose remote_agent_workflow_runs or events table lacks expected columns.

Related errors


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