windmill-labs/windmill · error

unable to find the module

Error message

unable to find the module

What it means

After parsing flow_status, the code searches its modules list for the module whose job() equals the requested job; if none matches, it throws 'unable to find the module'. This means the flow status exists but does not reference the given step/job — the resume target is not a module of that flow run.

Source

Thrown at backend/windmill-api/src/jobs.rs:5520

    // approvers are unaffected.
    if let Some(authed) = authed.as_ref() {
        require_job_within_run_scope(&db, authed, &w_id, &flow_id).await?;
    }

    let flow = GetQuery::new()
        .without_logs()
        .without_code()
        .fetch(&db, &flow_id, &w_id)
        .await?;

    let flow_status = flow
        .flow_status()
        .ok_or_else(|| anyhow::anyhow!("unable to find the flow status in the flow job"))?;
    let flow_module_status = flow_status
        .modules
        .iter()
        .find(|p| p.job() == Some(job))
        .ok_or_else(|| anyhow::anyhow!("unable to find the module"))?;

    let trigger_email = match &flow {
        Job::CompletedJob(job) => &job.email,
        Job::QueuedJob(job) => &job.email,
    };
    conditionally_require_authed_user(
        authed.clone(),
        flow_status.approval_conditions.clone(),
        trigger_email,
    )?;

    let approvers_from_status = match flow_module_status {
        FlowStatusModule::Success { approvers, .. } => approvers.to_owned(),
        _ => vec![],
    };
    let approvers = if approvers_from_status.is_empty() {
        sqlx::query!(
            r#"

View on GitHub (pinned to e474e8803c)

Solutions

  1. Confirm the job id and flow id come from the same run
  2. Re-issue the approval/resume link from the correct run
  3. Re-run the flow if the old run's module state no longer matches
  4. Avoid hard-coding resume ids; derive them from the current run's flow status

Example fix

// before
resume(wId, flowRunA_id, resumeIdFromRunB)
// after
resume(wId, flowRunB_id, resumeIdFromRunB)
Defensive patterns

Strategy: validation

Validate before calling

// make sure job and flow ids come from the same run
const run = await client.getJobRun(wId, flowJobId);
const module = (run.flow_status?.modules ?? []).find(m => m.job === stepJobId);
if (!module) throw new Error('step is not a module of this flow run');

Type guard

function stepBelongsToRun(flowStatus, stepJobId) {
  return Boolean(flowStatus?.modules?.some(m => m.job === stepJobId));
}

Prevention

When it happens

Trigger: Using a resume link/job id belonging to a different flow run than the flow_id supplied; a flow whose module status was overwritten by a later run of the same flow; approval step ids reused across reruns.

Common situations: Mixing ids from two concurrent runs of the same flow; approval link from run A applied to run B; flow edited and re-deployed so module layout no longer matches old statuses.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/cfcaae7e88ecf162. Report an issue: GitHub.