tinyhumansai/openhuman · error
workflow run {id} is already completed
Error message
workflow run {id} is already completed What it means
Thrown by workflow_runs::engine::resume_workflow_run when the run row exists but its status is already Completed. Completed is terminal; resume only makes sense for interrupted/failed/incomplete runs, and the check runs before the definition lookup and any state mutation.
Source
Thrown at src/openhuman/agent/orchestration/workflow_runs/engine.rs:296
log::debug!(target: LOG_TARGET, "[workflow_run_engine] stop.marked_interrupted run={id}");
Ok(Some(updated))
}
/// Resume an interrupted (or otherwise incomplete) workflow run.
///
/// Reloads the run, clears any stale cancellation flag, flips the row back to
/// `Running`, and spawns a fresh engine loop. Phases already `completed` in
/// `phase_states` are skipped; the loop continues from the first incomplete
/// phase whose dependencies are satisfied. Returns the run row (now `Running`),
/// or an error if the run is unknown / already terminal-complete / its
/// definition no longer exists.
pub async fn resume_workflow_run(config: &Config, id: &str) -> Result<WorkflowRun> {
log::debug!(target: LOG_TARGET, "[workflow_run_engine] resume.entry run={id}");
let run = get_workflow_run(&config.workspace_dir, id)?
.ok_or_else(|| anyhow!("unknown workflow run: {id}"))?;
if matches!(run.status, WorkflowRunStatus::Completed) {
return Err(anyhow!("workflow run {id} is already completed"));
}
let definition = definition_by_id(&run.definition_id)
.ok_or_else(|| anyhow!("definition {} no longer exists", run.definition_id))?;
// Clear any prior cancellation intent and re-register a fresh flag.
clear_cancel_flag(id);
register_cancel_flag(id);
let resumed = upsert_workflow_run(
&config.workspace_dir,
WorkflowRunUpsert {
id: run.id.clone(),
definition_id: run.definition_id.clone(),
parent_thread_id: run.parent_thread_id.clone(),
input: run.input.clone(),
phase_states: run.phase_states.clone(),
child_run_ids: run.child_run_ids.clone(),View on GitHub (pinned to a221052e0d)
Solutions
- Fetch the run and check status before resuming; skip Completed
- Make resume idempotent in the caller: treat 'already completed' as success
- Refresh the run list after any resume so the UI stops offering it
Example fix
// before
resume_workflow_run(&config, &run_id).await?;
// after — treat terminal-complete as a no-op
let run = get_workflow_run(&config.workspace_dir, &run_id)?
.ok_or_else(|| anyhow!("unknown workflow run: {run_id}"))?;
let run = match run.status {
WorkflowRunStatus::Completed => run, // already done — nothing to resume
_ => resume_workflow_run(&config, &run_id).await?,
}; Defensive patterns
Strategy: validation
Validate before calling
let run = get_workflow_run(&config.workspace_dir, run_id)?
.ok_or_else(|| anyhow!("unknown workflow run {run_id}"))?;
if matches!(run.status, WorkflowRunStatus::Completed) {
return Ok(run); // terminal — nothing to resume
} Type guard
fn is_resumable(run: &WorkflowRun) -> bool {
!matches!(run.status, WorkflowRunStatus::Completed)
} Try / catch
match resume_workflow_run(&config, run_id).await {
Ok(r) => Ok(r),
Err(e) if e.to_string().contains("already completed") => {
get_workflow_run(&config.workspace_dir, run_id)?.context("run vanished") // return final state
}
Err(e) => Err(e),
} Prevention
- Filter run lists by status before offering a Resume action (exclude Completed)
- Treat 'already completed' on a retried resume as success in caller retry logic
- Refresh run status after every resume attempt
When it happens
Trigger: Calling resume on a run that finished successfully — double-submit from a UI, a retry of a resume request whose first attempt completed the run, or an orchestrator that resumes everything in a list without filtering by status.
Common situations: Retry logic that treats any prior error (even one raised after completion) as 'resume needed'; stale run list in the UI still showing a Resume action; scripted bulk-resume over mixed-status runs.
Related errors
- unknown workflow definition: {definition_id}
- unknown workflow run: {id}
- definition {} no longer exists
- workflow run {run_id} vanished mid-loop
- workflow run {run_id} vanished mid-phase
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/baefcd38545ec2cf.
Report an issue: GitHub.