{"record":{"id":"baefcd38545ec2cf","repo":"tinyhumansai/openhuman","slug":"workflow-run-id-is-already-completed","errorCode":null,"errorMessage":"workflow run {id} is already completed","messagePattern":"workflow run (.+?) is already completed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/orchestration/workflow_runs/engine.rs","lineNumber":296,"sourceCode":"    log::debug!(target: LOG_TARGET, \"[workflow_run_engine] stop.marked_interrupted run={id}\");\n    Ok(Some(updated))\n}\n\n/// Resume an interrupted (or otherwise incomplete) workflow run.\n///\n/// Reloads the run, clears any stale cancellation flag, flips the row back to\n/// `Running`, and spawns a fresh engine loop. Phases already `completed` in\n/// `phase_states` are skipped; the loop continues from the first incomplete\n/// phase whose dependencies are satisfied. Returns the run row (now `Running`),\n/// or an error if the run is unknown / already terminal-complete / its\n/// definition no longer exists.\npub async fn resume_workflow_run(config: &Config, id: &str) -> Result<WorkflowRun> {\n    log::debug!(target: LOG_TARGET, \"[workflow_run_engine] resume.entry run={id}\");\n    let run = get_workflow_run(&config.workspace_dir, id)?\n        .ok_or_else(|| anyhow!(\"unknown workflow run: {id}\"))?;\n\n    if matches!(run.status, WorkflowRunStatus::Completed) {\n        return Err(anyhow!(\"workflow run {id} is already completed\"));\n    }\n\n    let definition = definition_by_id(&run.definition_id)\n        .ok_or_else(|| anyhow!(\"definition {} no longer exists\", run.definition_id))?;\n\n    // Clear any prior cancellation intent and re-register a fresh flag.\n    clear_cancel_flag(id);\n    register_cancel_flag(id);\n\n    let resumed = upsert_workflow_run(\n        &config.workspace_dir,\n        WorkflowRunUpsert {\n            id: run.id.clone(),\n            definition_id: run.definition_id.clone(),\n            parent_thread_id: run.parent_thread_id.clone(),\n            input: run.input.clone(),\n            phase_states: run.phase_states.clone(),\n            child_run_ids: run.child_run_ids.clone(),","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/orchestration/workflow_runs/engine.rs#L278-L314","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nresume_workflow_run(&config, &run_id).await?;\n\n// after — treat terminal-complete as a no-op\nlet run = get_workflow_run(&config.workspace_dir, &run_id)?\n    .ok_or_else(|| anyhow!(\"unknown workflow run: {run_id}\"))?;\nlet run = match run.status {\n    WorkflowRunStatus::Completed => run, // already done — nothing to resume\n    _ => resume_workflow_run(&config, &run_id).await?,\n};","handlingStrategy":"validation","validationCode":"let run = get_workflow_run(&config.workspace_dir, run_id)?\n    .ok_or_else(|| anyhow!(\"unknown workflow run {run_id}\"))?;\nif matches!(run.status, WorkflowRunStatus::Completed) {\n    return Ok(run); // terminal — nothing to resume\n}","typeGuard":"fn is_resumable(run: &WorkflowRun) -> bool {\n    !matches!(run.status, WorkflowRunStatus::Completed)\n}","tryCatchPattern":"match resume_workflow_run(&config, run_id).await {\n    Ok(r) => Ok(r),\n    Err(e) if e.to_string().contains(\"already completed\") => {\n        get_workflow_run(&config.workspace_dir, run_id)?.context(\"run vanished\") // return final state\n    }\n    Err(e) => Err(e),\n}","preventionTips":["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"],"tags":["workflow-runs","state-machine","idempotency"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}