windmill-labs/windmill · error
Timed out waiting for job ${jobId}
Error message
Timed out waiting for job ${jobId} What it means
When waiting for a cascade job (pipeline recording processing), the session polls the job status every CASCADE_POLL_INTERVAL_MS, tolerating transient errors, until it completes. If the job is still not completed when the polling budget is exhausted, it gives up with 'Timed out waiting for job <id>'.
Source
Thrown at frontend/src/lib/components/workspaceSettings/deployToHubSession.svelte.ts:1826
// timeout (DuckLake/DuckDB steps routinely exceed a few minutes). Adds the
// `#pipelineRunTok` cancellation the shared `makeWaitJobTerminal` lacks.
async #waitJobTerminal(jobId: string, tok: number): Promise<'success' | 'failure'> {
const deadline = Date.now() + CASCADE_JOB_TIMEOUT_MS
while (Date.now() < deadline) {
if (tok !== this.#pipelineRunTok) throw new Error('cancelled')
try {
const r = await JobService.getCompletedJobResultMaybe({
workspace: this.workspace,
id: jobId,
getStarted: false
})
if (r.completed) return r.success ? 'success' : 'failure'
} catch {
// transient — retry on the next tick
}
await sleep(CASCADE_POLL_INTERVAL_MS)
}
throw new Error(`Timed out waiting for job ${jobId}`)
}
/** Save the captured pipeline recording to the Hub, scoped to the project
* (a pipeline is the whole folder, not a single Hub item). Returns true on
* success. */
async savePipelineRecording(): Promise<boolean> {
const recording = this.pipelineRecordingResult
if (!recording || this.pipelineRunState !== 'success') return false
if (this.phase === 'predeploy') {
sendUserToast(`Push the project to the Hub first before saving its pipeline recording`, true)
return false
}
try {
await this.#postHub(`/hub/projects/${this.hubSlug}/pipeline_recording`, { recording })
this.pipelineRecorded = true
sendUserToast(`Pipeline recording saved`)
return true
} catch (e: any) {View on GitHub (pinned to e474e8803c)
Solutions
- Check the job's final state by id on the Hub — it may have completed after the client gave up
- Retry the operation (re-save the recording) if the job failed or is stuck
- Increase CASCADE_POLL_INTERVAL_MS budget/timeout if large recordings legitimately take longer
- Inspect Hub worker logs for the job id to find why it never completed
Defensive patterns
Strategy: retry
Try / catch
try {
await session.savePipelineRecording()
} catch (e) {
const m = /Timed out waiting for job (\S+)/.exec(String(e.message))
if (m) {
// re-query job status by m[1] — it may have completed after the client timed out
}
} Prevention
- Keep polling budgets larger than the worst-case processing time for large recordings
- Handle transient poll failures with backoff so the budget isn't consumed by retries
- If a job id is known after a timeout, check its final state server-side before re-running the operation
- Monitor Hub workers so jobs don't stall unnoticed
When it happens
Trigger: A Hub-side job (e.g. processing a saved pipeline recording) runs longer than the allotted polling window, or the polling requests keep failing transiently until the deadline passes.
Common situations: Hub backend busy or processing very large recordings; repeated network errors during polling; job stuck server-side (worker crashed) so it never reaches completed.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Timed out after ${Math.round(CASCADE_JOB_TIMEOUT_MS / 60000)
- ${timeoutMessage}
- reached timeout
- Job ${jobId} was not successful: ${JSON.stringify(error)}
- Timed out waiting for flow ${id} to complete
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/69a3cb8b559f4d0a.
Report an issue: GitHub.