windmill-labs/windmill · error · Error

Timed out waiting for job ${id}

Error message

Timed out waiting for job ${id}

What it means

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.

Source

Thrown at cli/src/commands/pipeline/pipeline.ts:430

): Promise<{ ok: boolean; result?: unknown }> {
  const MAX_RETRIES = 6000; // ~10min at 100ms
  for (let i = 0; i < MAX_RETRIES; i++) {
    try {
      const r = await wmill.getCompletedJobResultMaybe({
        workspace,
        id,
        getStarted: false,
      });
      // A completed job without an explicit `success: true` is a failure
      // (mirrors the frontend `waitJobTerminal`): the cascade only advances on
      // a confirmed success.
      if (r.completed) return { ok: r.success === true, result: r.result };
    } catch {
      // transient — retry
    }
    await new Promise((res) => setTimeout(res, 100));
  }
  throw new Error(`Timed out waiting for job ${id}`);
}

// Render a failed job's `{error: {name, message}}` result for the terminal, so
// the user sees WHY the cascade stopped without opening the UI. Result shapes
// vary (structured data-test payloads, plain strings), so fall back to raw
// JSON when the canonical error shape is absent.
function formatJobFailure(result: unknown): string | undefined {
  if (result == null) return undefined;
  const err = (result as any)?.error;
  if (err && typeof err === "object") {
    const name = typeof err.name === "string" ? err.name : undefined;
    const message = typeof err.message === "string" ? err.message : undefined;
    if (message) return name ? `${name}: ${message}` : message;
  }
  if (typeof result === "string") return result;
  try {
    return JSON.stringify(result, null, 2);
  } catch {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check the job in the Windmill UI (`/runs/get/<id>`) — if still running, increase the wait timeout option and re-run.
  2. If the job is suspended, approve/resume it in the UI and poll manually.
  3. Check worker availability (queue depth, worker count) if the job never started.
  4. Fetch the final result yourself with `wmill job get-result <id>` if the job has since completed.

Example fix

// before (CLI default wait)
wmill pipeline run f/pipeline

// after: longer wait via the command's timeout option or poll manually
wmill pipeline run f/pipeline --wait-timeout 3600
Defensive patterns

Strategy: retry

Validate before calling

// Nothing to validate pre-call (timeout is runtime); bound expectations instead:
// check the job exists and is not suspended before waiting
const job = await wmill.getJob({ workspace: ws, id });
if (job.type === "suspend") throw new Error("Job is suspended — approve it before waiting");

Try / catch

try {
  const r = await waitJob(id);
} catch (e: any) {
  if (String(e.message).startsWith("Timed out waiting for job")) {
    const jid = e.message.split("job ")[1];
    // inspect the job's real state before retrying with a longer budget
    const state = await wmill.getJob({ workspace: ws, id: jid });
    console.error(`Job ${jid} still ${state.running ? "running" : "queued/suspended"}; check the UI`);
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Understand the failure class

Related errors


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