ruvnet/ruflo · error · Error
Workflow cannot be resumed
Error message
Workflow cannot be resumed
What it means
Thrown by MaestroPlugin.resumeWorkflow() when the given workflowId is not present in the plugin's in-memory workflow registry, or when the workflow's status is anything other than 'paused'. Only workflows explicitly parked via pauseWorkflow() (which checkpoints state and sets status='paused') are resumable; resume simply re-enters executeWorkflow(). It is a lifecycle state-machine guard, not a crash: running, completed, failed, or unknown workflows all refuse to resume.
Source
Thrown at v3/@claude-flow/shared/src/plugins/official/maestro-plugin.ts:251
/**
* Pause a workflow
*/
pauseWorkflow(workflowId: string): boolean {
const workflow = this.workflows.get(workflowId);
if (!workflow || workflow.status !== 'running') return false;
this.checkpointWorkflow(workflow);
workflow.status = 'paused';
return true;
}
/**
* Resume a paused workflow
*/
async resumeWorkflow(workflowId: string): Promise<OrchestrationResult> {
const workflow = this.workflows.get(workflowId);
if (!workflow || workflow.status !== 'paused') {
throw new Error('Workflow cannot be resumed');
}
// Restore from checkpoint and continue
return this.executeWorkflow(workflowId);
}
/**
* Get workflow status
*/
getWorkflow(workflowId: string): Workflow | undefined {
return this.workflows.get(workflowId);
}
/**
* List all workflows
*/
listWorkflows(): Workflow[] {
return Array.from(this.workflows.values());View on GitHub (pinned to fa13ee4ad6)
Solutions
- Call and await pauseWorkflow(workflowId) first — only status 'paused' can be resumed.
- Guard the call: const wf = maestro.getWorkflow(workflowId); proceed only when wf && wf.status === 'paused'.
- If getWorkflow(id) is undefined, the id is wrong or in-memory state was lost — re-create the workflow instead of resuming.
- For terminal statuses ('completed', 'failed'), start a new workflow run rather than resuming the old one.
Example fix
// before
await maestro.resumeWorkflow(workflowId); // throws unless status === 'paused'
// after
const wf = maestro.getWorkflow(workflowId);
if (!wf) throw new Error(`unknown workflow ${workflowId}`);
if (wf.status !== 'paused') {
await maestro.pauseWorkflow(workflowId);
}
const result = await maestro.resumeWorkflow(workflowId); Defensive patterns
Strategy: validation
Validate before calling
const wf = maestro.getWorkflow(workflowId);
const resumable = !!wf && wf.status === 'paused';
if (!resumable) {
// pause the workflow first, or start a new run — do not call resumeWorkflow
} Type guard
function isResumableWorkflow(wf: Workflow | undefined): wf is Workflow & { status: 'paused' } {
return wf !== undefined && wf.status === 'paused';
} Try / catch
try {
await maestro.resumeWorkflow(workflowId);
} catch (e) {
if (e instanceof Error && e.message === 'Workflow cannot be resumed') {
const wf = maestro.getWorkflow(workflowId);
if (!wf) { /* unknown id: re-create the workflow */ }
else if (wf.status === 'completed' || wf.status === 'failed') { /* start a new run */ }
else { /* still running: wait, then pause + resume */ }
} else {
throw e;
}
} Prevention
- Drive resume affordances (buttons, API routes) from getWorkflow(id)?.status — only show them while 'paused'.
- Always pair pauseWorkflow and resumeWorkflow, and await pause before enabling resume.
- Workflow state is in-memory per plugin instance — persist ids and statuses externally to survive restarts.
When it happens
Trigger: Calling maestro.resumeWorkflow(id) while the workflow is still 'running' (pauseWorkflow never called or not yet awaited); resuming a 'completed' or 'failed' workflow; passing a workflowId that was never created with this plugin instance; calling resume after a process restart, since the workflows map is in-memory and the id no longer resolves.
Common situations: A resume button or API endpoint wired straight to resumeWorkflow without a status check; user clicks resume after the run already finished; plugin instance recreated on deploy/restart losing in-memory state; race where resume fires before pause completes.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- task step ${step.stepId} requires config.agentId or workflow
- agent_execute failed
- Issue ${issueId} is not claimed
- No pending handoff for issue ${issueId}
- worker dependency cycle: ${remaining.join(', ')}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/729f69398580b21e.
Report an issue: GitHub.