{"record":{"id":"6c1e9c5dbcfdfa1d","repo":"windmill-labs/windmill","slug":"job-not-found","errorCode":null,"errorMessage":"job not found: {}","messagePattern":"job not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-api/src/jobs.rs","lineNumber":6072,"sourceCode":"    }\n    Ok(job.runnable_path.unwrap_or_default())\n}\n\n/// Get the flow ID for a job. If the job is a flow, returns the job_id.\n/// If the job is a step in a flow, returns the parent flow ID.\nasync fn get_flow_id_for_job(db: &DB, job_id: Uuid) -> error::Result<Uuid> {\n    // First check if the job is a flow itself (kind = 'flow' or 'flowpreview')\n    let job_info = sqlx::query!(\n        r#\"\n        SELECT kind::text as \"kind!\", parent_job\n        FROM v2_job\n        WHERE id = $1\n        \"#,\n        job_id\n    )\n    .fetch_optional(db)\n    .await?\n    .ok_or_else(|| anyhow::anyhow!(\"job not found: {}\", job_id))?;\n\n    // If it's a flow job, return the job_id itself\n    if job_info.kind == \"flow\" || job_info.kind == \"flowpreview\" {\n        return Ok(job_id);\n    }\n\n    // Otherwise, return the parent flow ID\n    job_info\n        .parent_job\n        .ok_or_else(|| anyhow::anyhow!(\"job {} has no parent flow\", job_id).into())\n}\n\n#[derive(Deserialize)]\nstruct CancelJob {\n    reason: Option<String>,\n}\n\n#[derive(Debug, Deserialize)]","sourceCodeStart":6054,"sourceCodeEnd":6090,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-api/src/jobs.rs#L6054-L6090","documentation":"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).","triggerScenarios":"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.","commonSituations":"Hard-coded job ids in scripts surviving DB resets between environments; retention cleanup removing runs referenced by dashboards/automations; typos or truncated UUIDs.","solutions":["Verify the job id exists via the runs list or GET /jobs/w/{id}/run","Check you are querying the correct workspace","Handle 404 in callers so stale ids degrade gracefully","Adjust retention/cleanup if jobs disappear while still referenced"],"exampleFix":"// before\nconst job = await api.getJob(wId, '123e4567-...'); // throws if purged\n// after\nconst job = await api.getJob(wId, id).catch(e => e.status === 404 ? null : Promise.reject(e));\nif (!job) console.warn(`job ${id} no longer exists`);","handlingStrategy":"try-catch","validationCode":"// check existence first (list endpoint) to avoid a hard failure\nconst runs = await client.listRuns(wId, { job_id: jobId });\nif (runs.length === 0) console.warn('job not found, likely purged');","typeGuard":null,"tryCatchPattern":"try {\n  const flowId = await client.resolveJobFlow(wId, jobId);\n} catch (e) {\n  if (new RegExp('^job not found: ' + jobId).test(e.message)) {\n    return null; // treat as absent, e.g. purged by retention\n  }\n  throw e;\n}","preventionTips":["Don't hard-code job ids across environments or DB resets","Handle 404-style 'job not found' gracefully in dashboards/automations","Align retention/cleanup with how long you reference old runs"],"tags":["job","not-found","database","id"],"backgroundTag":"job-not-found","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}