windmill-labs/windmill · error

Timed out after ${Math.round(CASCADE_JOB_TIMEOUT_MS / 60000)

Error message

Timed out after ${Math.round(CASCADE_JOB_TIMEOUT_MS / 60000)}min waiting for job ${jobId} to finish

What it means

cascadeRun.ts polls a launched Windmill job until it reaches a terminal state (success/failure/completed). makeWaitJobTerminal throws this timeout error when the job has not become terminal within CASCADE_JOB_TIMEOUT_MS (expressed in minutes in the message), while tolerating transient polling errors.

Source

Thrown at frontend/src/lib/components/assets/AssetGraph/cascadeRun.ts:56

export function makeWaitJobTerminal(
	workspace: string
): (jobId: string) => Promise<'success' | 'failure'> {
	return async function waitJobTerminal(jobId: string): Promise<'success' | 'failure'> {
		const deadline = Date.now() + CASCADE_JOB_TIMEOUT_MS
		while (Date.now() < deadline) {
			try {
				const r = await JobService.getCompletedJobResultMaybe({
					workspace,
					id: jobId,
					getStarted: false
				})
				if (r.completed) return r.success ? 'success' : 'failure'
			} catch {
				// transient — retry on the next tick
			}
			await new Promise((res) => setTimeout(res, CASCADE_POLL_INTERVAL_MS))
		}
		throw new Error(
			`Timed out after ${Math.round(CASCADE_JOB_TIMEOUT_MS / 60000)}min waiting for job ${jobId} to finish`
		)
	}
}

// Build a per-script launch function. When `resolveLocal(path)` yields content,
// the script runs as a preview of that local content (no deploy); otherwise it
// runs the deployed version by path. Always passes `_wmill_skip_asset_dispatch`.
export function makeLaunch(opts: {
	workspace: string
	resolveLocal?: (path: string) => LocalScriptContent | undefined
	tempScriptRefs?: Record<string, string>
	// Extra run args for a specific node (e.g. the uploaded S3Object bound to a
	// `data_upload` cascade root). Merged over `_wmill_skip_asset_dispatch`; all
	// other nodes run with empty inputs as before.
	argsFor?: (path: string) => Record<string, any> | undefined
	onLaunched?: (path: string, jobId: string) => void
}): (path: string) => Promise<string> {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check the job ${jobId} in the Windmill UI runs view to see if it is queued, running, hung, or failed silently
  2. Scale up workers / clear the queue so the job actually runs within the timeout window
  3. Add an internal timeout or `timeout` setting to the script itself so a hung step fails terminally instead of running forever
  4. Increase CASCADE_JOB_TIMEOUT_MS in cascadeRun.ts if your jobs legitimately take longer than the default
  5. Verify the poll requests hit the right workspace and aren't erroring permanently (the catch treats all poll failures as transient)
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check job state before committing to the wait
const job = await JobService.getJob({ workspace, id: jobId })
if (job.completed && !job.success) throw new Error(`job ${jobId} already failed: ${job.logs ?? ''}`)

Type guard

function isTerminal(job: { completed?: boolean }): job is { completed: true } {
  return job.completed === true
}

Try / catch

try {
  await waitJobTerminal(jobId)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Timed out after')) {
    // inspect the job in the UI, cancel or retry with a longer window
    await JobService.cancelJob({ workspace, id: jobId }).catch(() => {})
    throw new Error(`cascade job ${jobId} hung; cancelled`, { cause: e })
  }
  throw e
}

Prevention

When it happens

Trigger: A run triggered by the asset-graph cascade launched a job whose id never reaches a completed state before the timeout window elapses — the job is queued for a long time, the worker pool is saturated, the job hangs (e.g. waiting on an external service), or the workspace/job was cancelled in a way that never marks completion.

Common situations: Long queue on a self-hosted instance with few workers; a script stuck on a slow API call without its own timeout; gateway/auth issues making the poll endpoint permanently return non-completed states (poll errors are swallowed as 'transient'); running against the wrong workspace so the completed job is never seen.

Understand the failure class

Related errors


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