windmill-labs/windmill · error · Error

${timeoutMessage}

Error message

${timeoutMessage}

What it means

runWithProgress enforces a timeout (default 60s, configurable via JobOptions.timeout). When tryEvery's deadline expires, the timeoutCode callback first attempts to cancel the queued job server-side (JobService.cancelQueuedJob), reports onProgress({status:'failure'}), and throws Error(timeoutMessage), by default `Job timed out after Ns`.

Source

Thrown at frontend/src/lib/services/JobManager.ts:124

					onProgress?.(status)

					finalResult = jobResult.result as T

					return jobResult.result as T
				},
				timeoutCode: async () => {
					try {
						await JobService.cancelQueuedJob({
							workspace,
							id: jobId,
							requestBody: { reason: timeoutMessage }
						})
					} catch (err) {
						console.error('Failed to cancel job:', err)
					}

					onProgress?.({ status: 'failure', error: timeoutMessage })
					throw new Error(timeoutMessage)
				},
				interval,
				timeout
			})

			return finalResult as T
		} finally {
			this.activeJobs.delete(jobId)
		}
	}

	cancel(jobId: string) {
		const entry = this.activeJobs.get(jobId)
		if (entry) {
			entry.controller.abort()
			this.activeJobs.delete(jobId)
		}
	}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Increase the timeout option passed to runWithProgress to exceed the job's expected duration
  2. Investigate why the job is slow (worker availability, job logs in the Windmill UI)
  3. Reduce the job's work or split it into smaller steps
  4. Handle the timeout error in the caller and offer retry/extend UI

Example fix

// before
const result = await jobManager.runWithProgress(runJob, { workspace, onProgress }) // default 60s timeout
// after
const result = await jobManager.runWithProgress(runJob, {
  workspace,
  onProgress,
  timeout: 300000, // 5 min
  timeoutMessage: 'Generation timed out after 5 minutes'
})
Defensive patterns

Strategy: retry

Validate before calling

// size the timeout to the job before starting
const expectedMs = estimateJobDuration(jobKind)
if (expectedMs > (options.timeout ?? 60000)) {
  options.timeout = expectedMs * 2
}

Try / catch

try {
  const result = await jobManager.runWithProgress(runJob, { ...opts, timeout: 300000 })
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Job timed out')) {
    onTimeout(err.message) // offer retry or poll the job manually via JobService
    return
  }
  throw err
}

Prevention

When it happens

Trigger: The Windmill job (script/flow/AI generation) takes longer than options.timeout to complete while getCompletedJob keeps returning a non-completed job; interval polling never sees success before the deadline.

Common situations: Long-running flows or heavy scripts polled from the UI with the default 60s timeout; slow worker queues under load; AI generation jobs exceeding the default; passing a timeout smaller than the job's realistic duration.

Understand the failure class

Related errors


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