{"record":{"id":"776d3910632d234d","repo":"windmill-labs/windmill","slug":"timed-out-waiting-for-job-id","errorCode":null,"errorMessage":"Timed out waiting for job ${id}","messagePattern":"Timed out waiting for job (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/pipeline/pipeline.ts","lineNumber":430,"sourceCode":"): Promise<{ ok: boolean; result?: unknown }> {\n  const MAX_RETRIES = 6000; // ~10min at 100ms\n  for (let i = 0; i < MAX_RETRIES; i++) {\n    try {\n      const r = await wmill.getCompletedJobResultMaybe({\n        workspace,\n        id,\n        getStarted: false,\n      });\n      // A completed job without an explicit `success: true` is a failure\n      // (mirrors the frontend `waitJobTerminal`): the cascade only advances on\n      // a confirmed success.\n      if (r.completed) return { ok: r.success === true, result: r.result };\n    } catch {\n      // transient — retry\n    }\n    await new Promise((res) => setTimeout(res, 100));\n  }\n  throw new Error(`Timed out waiting for job ${id}`);\n}\n\n// Render a failed job's `{error: {name, message}}` result for the terminal, so\n// the user sees WHY the cascade stopped without opening the UI. Result shapes\n// vary (structured data-test payloads, plain strings), so fall back to raw\n// JSON when the canonical error shape is absent.\nfunction formatJobFailure(result: unknown): string | undefined {\n  if (result == null) return undefined;\n  const err = (result as any)?.error;\n  if (err && typeof err === \"object\") {\n    const name = typeof err.name === \"string\" ? err.name : undefined;\n    const message = typeof err.message === \"string\" ? err.message : undefined;\n    if (message) return name ? `${name}: ${message}` : message;\n  }\n  if (typeof result === \"string\") return result;\n  try {\n    return JSON.stringify(result, null, 2);\n  } catch {","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/pipeline/pipeline.ts#L412-L448","documentation":"Thrown by waitJob when polling a Windmill job (scheduled pipeline run) exceeds the wait budget without the job reaching a completed state. The helper polls job status every 100ms (transient fetch errors are swallowed and retried); if the loop exhausts, it gives up with this timeout error naming the job id.","triggerScenarios":"Running a pipeline whose jobs take longer than waitJob's polling limit (long-running scripts, stuck/queued jobs on a saturated worker), or a job id that will never complete because it is suspended (waiting for approval/sleep) or was cancelled while the poller kept missing completion.","commonSituations":"Pipelines with heavy S3/data steps exceeding the default timeout; all workers busy so the job sits in queue; a suspended (human-approval) step never approved; wrong instance checked (job completed on another workspace).","solutions":["Check the job in the Windmill UI (`/runs/get/<id>`) — if still running, increase the wait timeout option and re-run.","If the job is suspended, approve/resume it in the UI and poll manually.","Check worker availability (queue depth, worker count) if the job never started.","Fetch the final result yourself with `wmill job get-result <id>` if the job has since completed."],"exampleFix":"// before (CLI default wait)\nwmill pipeline run f/pipeline\n\n// after: longer wait via the command's timeout option or poll manually\nwmill pipeline run f/pipeline --wait-timeout 3600","handlingStrategy":"retry","validationCode":"// Nothing to validate pre-call (timeout is runtime); bound expectations instead:\n// check the job exists and is not suspended before waiting\nconst job = await wmill.getJob({ workspace: ws, id });\nif (job.type === \"suspend\") throw new Error(\"Job is suspended — approve it before waiting\");","typeGuard":null,"tryCatchPattern":"try {\n  const r = await waitJob(id);\n} catch (e: any) {\n  if (String(e.message).startsWith(\"Timed out waiting for job\")) {\n    const jid = e.message.split(\"job \")[1];\n    // inspect the job's real state before retrying with a longer budget\n    const state = await wmill.getJob({ workspace: ws, id: jid });\n    console.error(`Job ${jid} still ${state.running ? \"running\" : \"queued/suspended\"}; check the UI`);\n  }\n  throw e;\n}","preventionTips":["Increase the wait timeout for pipelines with known-long steps.","Check worker availability/queue depth if jobs routinely sit queued.","Never point waitJob at suspended jobs — approve them via the UI or API first.","Inspect /runs/get/<id> in the UI to distinguish slow vs stuck jobs."],"tags":["timeout","cli","polling"],"backgroundTag":"job-wait-timeout","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"}