windmill-labs/windmill · error
job not found or parent flow not in queue: {}
Error message
job not found or parent flow not in queue: {} What it means
Resuming a step requires its parent flow job to exist and be currently sitting in the queue (locked FOR UPDATE on the queue row). This error is thrown when the query finds no such job: the job id is wrong, the flow is not queued, or the parent flow row cannot be locked.
Source
Thrown at backend/windmill-api/src/jobs.rs:5370
q.suspend AS "suspend!",
j.runnable_path AS script_path,
j.permissioned_as_email AS email,
(ji.kind IN ('flow', 'flowpreview', 'singlestepflow')) AS "is_flow_level!",
(ji.kind NOT IN ('flow', 'flowpreview', 'singlestepflow') AND q.id = ji.id) AS "is_wac!"
FROM job_info ji
JOIN v2_job_queue q ON q.id = CASE
WHEN ji.kind IN ('flow', 'flowpreview', 'singlestepflow') THEN ji.id
ELSE COALESCE(ji.parent_job, ji.id)
END
JOIN v2_job j ON j.id = q.id
LEFT JOIN v2_job_status s ON s.id = q.id
FOR UPDATE OF q
"#,
job_id,
)
.fetch_optional(db)
.await?
.ok_or_else(|| anyhow::anyhow!("job not found or parent flow not in queue: {}", job_id))?;
let flow_info = FlowInfo {
id: result.id,
flow_status: result.flow_status,
suspend: result.suspend,
script_path: result.script_path,
email: Some(result.email),
};
Ok((flow_info, result.is_flow_level, result.is_wac))
}
async fn get_suspended_flow_info<'c>(
job_id: Uuid,
tx: &mut Transaction<'c, Postgres>,
) -> error::Result<(FlowInfo, Uuid, bool)> {
let flow = sqlx::query_as!(
FlowInfo,View on GitHub (pinned to e474e8803c)
Solutions
- Verify the flow run is still suspended/queued in the runs UI before resuming
- Use the correct job identifier for the suspended step (not the completed run's id)
- Re-run the flow if it already completed or was canceled — resumption is impossible
- Check that the parent flow job was not purged by retention/cleanup policies
Example fix
// before resumeFlow(wId, completedRunId, resumeId) // throws // after const run = await getRun(wId, flowId); if (run.state === 'suspended') resumeFlow(wId, flowId, resumeId);
Defensive patterns
Strategy: validation
Validate before calling
// only attempt resume when the flow run is still waiting
const run = await client.getJobRun(wId, flowJobId);
if (run.state !== 'suspended' && run.state !== 'waiting') {
throw new Error('flow not resumable (state: ' + run.state + ')');
} Try / catch
try {
await client.resumeStep(wId, flowJobId, resumeId, payload);
} catch (e) {
if (/job not found or parent flow not in queue/.test(e.message)) {
// run already finished/canceled: surface as a no-op, don't retry
} else throw e;
} Prevention
- Check run state before resuming
- Don't reuse resume URLs after the run completes
- Distinguish step job ids from flow job ids
When it happens
Trigger: POSTing a resume for a step whose parent flow already finished; resuming a job id that belongs to a script, not a suspended flow step; the parent flow was canceled or purged before the resume arrived.
Common situations: Calling the resume API with the step job id instead of an id resolvable through a queued parent flow; racing a canceled flow; stale webhook URLs after the run completed.
Related errors
- parent flow job not found
- Flow ${flowPath} not found
- the flow is not in a suspended state anymore
- unable to find the flow status in the flow job
- App ${appPath} not found
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/d35e755054e3cc85.
Report an issue: GitHub.