invoke-ai/InvokeAI · info · DownloadJobCancelledException

Job was cancelled before start

Error message

Job was cancelled before start

What it means

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.

Source

Thrown at invokeai/app/services/download/download_default.py:337

            worker = threading.Thread(target=self._download_next_item, daemon=True)
            self._logger.debug(f"Download queue worker thread {worker.name} starting.")
            worker.start()
            self._worker_pool.add(worker)

    def _download_next_item(self) -> None:
        """Worker thread gets next job on priority queue."""
        done = False
        while not done:
            if self._stop_event.is_set():
                done = True
                continue
            try:
                job = self._queue.get(timeout=1)
            except Empty:
                continue
            try:
                if job.cancelled:
                    raise DownloadJobCancelledException("Job was cancelled before start")
                job.job_started = get_iso_timestamp()
                self._do_download(job)
                if job.status != DownloadJobStatus.COMPLETED:
                    self._signal_job_complete(job)
            except DownloadJobCancelledException:
                if job.paused:
                    self._signal_job_paused(job)
                else:
                    self._signal_job_cancelled(job)
                    self._cleanup_cancelled_job(job)
            except Exception as excp:
                job.error_type = excp.__class__.__name__ + f"({str(excp)})"
                job.error = traceback.format_exc()
                self._signal_job_error(job, excp)
            finally:
                job.job_ended = get_iso_timestamp()
                self._job_terminated_event.set()  # signal a change to terminal state
                self._download_part2parent.pop(job.id, None)  # if this is a subpart of a multipart job, remove it

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. No fix needed — this is the expected internal cancellation path; cancellation callbacks (on_cancelled) will fire
  2. If you see it propagate in custom worker code, ensure exceptions derive from DownloadJobCancelledException are handled like the default worker does
  3. Avoid calling cancel_job() concurrently with queue mutation if you don't need pre-start cancellation

Example fix

// before (custom worker)
job = queue.get()
self._do_download(job)
// after

job = queue.get()
if job.cancelled:
    raise DownloadJobCancelledException("Job was cancelled before start")
self._do_download(job)
Defensive patterns

Strategy: try-catch

Validate before calling

def job_is_cancellable(job) -> bool:
    return not job.in_terminal_state and not job.cancelled
# only call cancel_job when job_is_cancellable(job)

Type guard

def is_active_job(job) -> bool:
    return not job.cancelled and not job.in_terminal_state

Try / catch

try:
    self._do_download(job)
except DownloadJobCancelledException:
    job.status = DownloadJobStatus.CANCELLED
    job.on_cancelled(job)

Prevention

When it happens

Trigger: 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.

Common situations: User cancels a queued download via the UI/API before a worker picks it up; burst of queued downloads cancelled en masse during shutdown.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/355cf45448a5bfb4. Report an issue: GitHub.