{"record":{"id":"6565b2922bbfe353","repo":"windmill-labs/windmill","slug":"job-was-cancelled","errorCode":null,"errorMessage":"Job was cancelled","messagePattern":"Job was cancelled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/src/lib/services/JobManager.ts","lineNumber":86,"sourceCode":"\t\t} = options\n\n\t\tconst controller = new AbortController()\n\t\tconst jobId = await jobRunner()\n\n\t\tthis.activeJobs.set(jobId, {\n\t\t\tcontroller,\n\t\t\tstartTime: Date.now()\n\t\t})\n\n\t\ttry {\n\t\t\tonProgress?.({ status: 'running' })\n\n\t\t\tlet finalResult: T | undefined = undefined\n\n\t\t\tawait tryEvery({\n\t\t\t\ttryCode: async () => {\n\t\t\t\t\tif (controller.signal.aborted) {\n\t\t\t\t\t\tthrow new Error('Job was cancelled')\n\t\t\t\t\t}\n\n\t\t\t\t\tlet jobResult\n\t\t\t\t\ttry {\n\t\t\t\t\t\tjobResult = await JobService.getCompletedJob({\n\t\t\t\t\t\t\tworkspace,\n\t\t\t\t\t\t\tid: jobId\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\n\t\t\t\t\tconst success = !!jobResult.success\n\t\t\t\t\tconst status: JobStatus = {\n\t\t\t\t\t\tstatus: success ? 'success' : 'failure',\n\t\t\t\t\t\tresult: jobResult.result,\n\t\t\t\t\t\terror: success ? undefined : (jobResult.result as any)?.error?.message || 'Job failed'\n\t\t\t\t\t}","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/services/JobManager.ts#L68-L104","documentation":"JobManager.runWithProgress polls a Windmill job until completion. On each poll tick it checks the AbortController created for the run; if the job was cancelled (via cancel(jobId) or cancelAll(), which call controller.abort()), the next poll throws Error('Job was cancelled') and the promise rejects.","triggerScenarios":"Calling runWithProgress, then invoking jobManager.cancel(jobId) or cancelAll() (e.g. component teardown, navigation away, user pressing a cancel button) while the polling loop is still running; or the abort signal already aborted when a poll tick fires.","commonSituations":"Svelte component destroyed mid-run without awaiting; user navigating away from an AI-generation/preview panel; a timeout elsewhere aborting all jobs; race where abort happens between job start and first poll.","solutions":["Wrap the runWithProgress call in try/catch and treat Error with message 'Job was cancelled' as an expected, non-alerting outcome","Avoid calling cancelAll()/cancel() on teardown if you still need the result, or await the promise before teardown","Check controller state / isActive(jobId) before relying on the result"],"exampleFix":"// before\nconst result = await jobManager.runWithProgress(runJob, opts)\n// after\ntry {\n  const result = await jobManager.runWithProgress(runJob, opts)\n} catch (err) {\n  if (err instanceof Error && err.message === 'Job was cancelled') return // expected on teardown\n  throw err\n}","handlingStrategy":"try-catch","validationCode":"// before awaiting, ensure no cancellation is pending\nif (jobManager.isActive(jobId) === false || controller?.signal.aborted) {\n  return // skip the run\n}","typeGuard":"function isCancellationError(err: unknown): err is Error {\n  return err instanceof Error && err.message === 'Job was cancelled'\n}","tryCatchPattern":"try {\n  const result = await jobManager.runWithProgress(runJob, opts)\n} catch (err) {\n  if (isCancellationError(err)) {\n    return null // user-initiated or teardown cancellation: handle silently\n  }\n  throw err\n}","preventionTips":["Treat 'Job was cancelled' as an expected control-flow signal, not a failure to report","Cancel jobs deliberately on component teardown and handle the resulting rejection","Avoid calling cancelAll() when individual runs' results are still needed","Track jobIds you cancel so you can correlate the rejection"],"tags":["abort","cancellation","async","polling"],"backgroundTag":"operation-aborted","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}