windmill-labs/windmill · error

job not found: {}

Error message

job not found: {}

What it means

A helper that resolves a job (and, for flow steps, its flow id) looks up the jobs table by id; when fetch_optional returns None it throws 'job not found: {id}'. It simply means no job with that id exists in the database (in that workspace context).

Source

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

    }
    Ok(job.runnable_path.unwrap_or_default())
}

/// Get the flow ID for a job. If the job is a flow, returns the job_id.
/// If the job is a step in a flow, returns the parent flow ID.
async fn get_flow_id_for_job(db: &DB, job_id: Uuid) -> error::Result<Uuid> {
    // First check if the job is a flow itself (kind = 'flow' or 'flowpreview')
    let job_info = sqlx::query!(
        r#"
        SELECT kind::text as "kind!", parent_job
        FROM v2_job
        WHERE id = $1
        "#,
        job_id
    )
    .fetch_optional(db)
    .await?
    .ok_or_else(|| anyhow::anyhow!("job not found: {}", job_id))?;

    // If it's a flow job, return the job_id itself
    if job_info.kind == "flow" || job_info.kind == "flowpreview" {
        return Ok(job_id);
    }

    // Otherwise, return the parent flow ID
    job_info
        .parent_job
        .ok_or_else(|| anyhow::anyhow!("job {} has no parent flow", job_id).into())
}

#[derive(Deserialize)]
struct CancelJob {
    reason: Option<String>,
}

#[derive(Debug, Deserialize)]

View on GitHub (pinned to e474e8803c)

Solutions

  1. Verify the job id exists via the runs list or GET /jobs/w/{id}/run
  2. Check you are querying the correct workspace
  3. Handle 404 in callers so stale ids degrade gracefully
  4. Adjust retention/cleanup if jobs disappear while still referenced

Example fix

// before
const job = await api.getJob(wId, '123e4567-...'); // throws if purged
// after
const job = await api.getJob(wId, id).catch(e => e.status === 404 ? null : Promise.reject(e));
if (!job) console.warn(`job ${id} no longer exists`);
Defensive patterns

Strategy: try-catch

Validate before calling

// check existence first (list endpoint) to avoid a hard failure
const runs = await client.listRuns(wId, { job_id: jobId });
if (runs.length === 0) console.warn('job not found, likely purged');

Try / catch

try {
  const flowId = await client.resolveJobFlow(wId, jobId);
} catch (e) {
  if (new RegExp('^job not found: ' + jobId).test(e.message)) {
    return null; // treat as absent, e.g. purged by retention
  }
  throw e;
}

Prevention

When it happens

Trigger: Querying the job/flow-resolution endpoint with an id that was never created, already purged, or from another workspace; the run being deleted between listing and fetching.

Common situations: Hard-coded job ids in scripts surviving DB resets between environments; retention cleanup removing runs referenced by dashboards/automations; typos or truncated UUIDs.

Related errors


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