windmill-labs/windmill · error

errorMsg ?? 'Job failed'

Error message

errorMsg ?? 'Job failed'

What it means

pollJobResult polls a script/job until completion and throws when the finished job's result contains an error, preferring the job's own error.message and falling back to 'Job failed'. It converts an async job failure into a rejected promise for the caller.

Source

Thrown at frontend/src/lib/components/jobs/utils.ts:112

			await new Promise((resolve) =>
				setTimeout(resolve, attempts ? 500 * attempts : pollDelayMs(polls++))
			)
			const job = await JobService.getCompletedJobResultMaybe({
				id: uuid,
				workspace
			})
			if (job.success) {
				if (withJobData) {
					return { job: { id: uuid }, result: job.result }
				} else {
					return job.result as any
				}
			} else if (job.completed) {
				attempts = maxRetries
				let errorMsg: string | undefined = (job?.result as any)?.error?.message
				if (typeof errorMsg !== 'string') errorMsg = undefined
				console.error('JOB FAILED', job.result)
				throw new Error(errorMsg ?? 'Job failed')
			} else if (failIfNoWorkerForTag && Date.now() >= noWorkerProbeAt) {
				const tag = await missingWorkerTagOfQueuedJob(workspace, uuid)
				noWorkerProbeAt = Date.now() + NO_WORKER_PROBE_INTERVAL_MS
				unservedProbes = tag ? unservedProbes + 1 : 0
				if (tag && unservedProbes >= NO_WORKER_CONFIRMATIONS) {
					if (!reportedNoWorker) {
						reportedNoWorker = true
						onNoWorkerForTag?.(tag)
					}
					// Reads give up the wait but leave the job queued: cancelling one would
					// remove the very backlog the autoscaler scales up on, so a group coming
					// back from zero (300s cooldown) would never recover. Writes keep
					// waiting instead (see `sideEffecting`).
					if (!sideEffecting) throw new NoWorkerForTagError(tag)
				}
			}
		} catch (e) {
			if (e instanceof NoWorkerForTagError) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Read the thrown message (job result error.message) and fix the script's root cause; full result is logged via console.error('JOB FAILED', ...).
  2. Open the run in the UI run history to see the stack trace and logs.
  3. If the message is the generic 'Job failed', inspect the run logs server-side for the real exception.
  4. Add retry/validation in the script for transient inputs, and handle the rejection at the call site.

Example fix

// before
await pollJobResult(ws, uuid)
// after
try { await pollJobResult(ws, uuid) } catch (e) { console.error('run failed:', e.message) }
Defensive patterns

Strategy: try-catch

Type guard

function jobHasErrorMessage(job) { return typeof (job?.result as any)?.error?.message === 'string' }

Try / catch

try { const result = await pollJobResult(ws, uuid) } catch (e) { showError(e.message); openRunLogs(uuid) }

Prevention

When it happens

Trigger: A polled job ends with status failure/completed-with-error: run failed (unhandled exception in the script), or the script returned { error: { message } } in its result.

Common situations: Script code raised (bad input, missing dependency, timeout); the job was denied/queued into failure by worker errors; API responses where result.error.message is absent so only the generic message appears.

Related errors


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