{"record":{"id":"355cf45448a5bfb4","repo":"invoke-ai/InvokeAI","slug":"job-was-cancelled-before-start","errorCode":null,"errorMessage":"Job was cancelled before start","messagePattern":"Job was cancelled before start","errorType":"exception","errorClass":"DownloadJobCancelledException","httpStatus":null,"severity":"info","filePath":"invokeai/app/services/download/download_default.py","lineNumber":337,"sourceCode":"            worker = threading.Thread(target=self._download_next_item, daemon=True)\n            self._logger.debug(f\"Download queue worker thread {worker.name} starting.\")\n            worker.start()\n            self._worker_pool.add(worker)\n\n    def _download_next_item(self) -> None:\n        \"\"\"Worker thread gets next job on priority queue.\"\"\"\n        done = False\n        while not done:\n            if self._stop_event.is_set():\n                done = True\n                continue\n            try:\n                job = self._queue.get(timeout=1)\n            except Empty:\n                continue\n            try:\n                if job.cancelled:\n                    raise DownloadJobCancelledException(\"Job was cancelled before start\")\n                job.job_started = get_iso_timestamp()\n                self._do_download(job)\n                if job.status != DownloadJobStatus.COMPLETED:\n                    self._signal_job_complete(job)\n            except DownloadJobCancelledException:\n                if job.paused:\n                    self._signal_job_paused(job)\n                else:\n                    self._signal_job_cancelled(job)\n                    self._cleanup_cancelled_job(job)\n            except Exception as excp:\n                job.error_type = excp.__class__.__name__ + f\"({str(excp)})\"\n                job.error = traceback.format_exc()\n                self._signal_job_error(job, excp)\n            finally:\n                job.job_ended = get_iso_timestamp()\n                self._job_terminated_event.set()  # signal a change to terminal state\n                self._download_part2parent.pop(job.id, None)  # if this is a subpart of a multipart job, remove it","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/download/download_default.py#L319-L355","documentation":"Worker thread _download_next_item() raises DownloadJobCancelledException('Job was cancelled before start') when it dequeues a job whose `cancelled` flag is already set. The exception is handled internally to route the job to its cancellation callbacks — callers normally never see it thrown outward.","triggerScenarios":"cancel_job() was called (setting job.cancelled) while the job was still sitting in the queue, then a worker dequeued it and detected the flag before running the download.","commonSituations":"User cancels a queued download via the UI/API before a worker picks it up; burst of queued downloads cancelled en masse during shutdown.","solutions":["No fix needed — this is the expected internal cancellation path; cancellation callbacks (on_cancelled) will fire","If you see it propagate in custom worker code, ensure exceptions derive from DownloadJobCancelledException are handled like the default worker does","Avoid calling cancel_job() concurrently with queue mutation if you don't need pre-start cancellation"],"exampleFix":"// before (custom worker)\njob = queue.get()\nself._do_download(job)\n// after\n\njob = queue.get()\nif job.cancelled:\n    raise DownloadJobCancelledException(\"Job was cancelled before start\")\nself._do_download(job)","handlingStrategy":"try-catch","validationCode":"def job_is_cancellable(job) -> bool:\n    return not job.in_terminal_state and not job.cancelled\n# only call cancel_job when job_is_cancellable(job)","typeGuard":"def is_active_job(job) -> bool:\n    return not job.cancelled and not job.in_terminal_state","tryCatchPattern":"try:\n    self._do_download(job)\nexcept DownloadJobCancelledException:\n    job.status = DownloadJobStatus.CANCELLED\n    job.on_cancelled(job)","preventionTips":["Check job.cancelled before running custom worker logic","Register on_cancelled callbacks so pre-start cancellation is observed","Don't treat DownloadJobCancelledException as a failure — it's the normal cancel path","Avoid double-cancelling jobs already flagged cancelled"],"tags":["download","cancellation","worker","queue"],"backgroundTag":"job-cancelled-before-start","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}