invoke-ai/InvokeAI · error · TimeoutError

Timeout exceeded

Error message

Timeout exceeded

What it means

wait_for_job() blocks polling a single ModelInstallJob until it reaches terminal state. If the job does not finish within the supplied timeout (seconds), it raises TimeoutError('Timeout exceeded'). The job itself may still be running or stuck (e.g. slow or stalled download).

Source

Thrown at invokeai/app/services/model_install/model_install_default.py:573

    def get_job_by_source(self, source: ModelSource) -> List[ModelInstallJob]:  # noqa D102
        return [x for x in self._install_jobs if x.source == source]

    def get_job_by_id(self, id: int) -> ModelInstallJob:  # noqa D102
        jobs = [x for x in self._install_jobs if x.id == id]
        if not jobs:
            raise ValueError(f"No job with id {id} known")
        assert len(jobs) == 1
        assert isinstance(jobs[0], ModelInstallJob)
        return jobs[0]

    def wait_for_job(self, job: ModelInstallJob, timeout: int = 0) -> ModelInstallJob:
        """Block until the indicated job has reached terminal state, or when timeout limit reached."""
        start = time.time()
        while not job.in_terminal_state:
            if self._install_completed_event.wait(timeout=5):  # in case we miss an event
                self._install_completed_event.clear()
            if timeout > 0 and time.time() - start > timeout:
                raise TimeoutError("Timeout exceeded")
        return job

    def wait_for_installs(self, timeout: int = 0) -> List[ModelInstallJob]:  # noqa D102
        """Block until all installation jobs are done."""
        start = time.time()
        restore_timeout = timeout if timeout > 0 else None
        if not self._wait_for_restore_complete(timeout=restore_timeout):
            raise TimeoutError("Timeout exceeded")

        while True:
            # The completion callback removes a download from this cache while holding
            # the same lock it uses to enqueue the install. Do not observe the cache
            # between those two operations.
            with self._lock:
                downloads_pending = bool(self._download_cache)
            if not downloads_pending:
                break
            if self._downloads_changed_event.wait(timeout=0.25):  # in case we miss an event

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Increase the timeout parameter to accommodate the model size and network speed (or pass 0 to wait indefinitely).
  2. Instead of blocking, poll the job's status periodically and handle progress/cancel.
  3. Check network/disk issues or the install queue if the job appears permanently stuck.
  4. On timeout, inspect job.status/error via get_job_by_id() before retrying; resume via restart_job if failed.

Example fix

// before
job = service.wait_for_job(install_job, timeout=30)
// after
job = service.wait_for_job(install_job, timeout=1800)  # allow 30 min for large downloads
Defensive patterns

Strategy: try-catch

Validate before calling

# estimate: ensure timeout scales with expected download size
effective_timeout = max(timeout, estimated_bytes / bytes_per_second)

Try / catch

try:
    job = service.wait_for_job(install_job, timeout=1800)
except TimeoutError:
    job = service.get_job_by_id(install_job.id)  # inspect current status instead of assuming failure

Prevention

When it happens

Trigger: Calling wait_for_job(job, timeout=N) where the install/download takes longer than N seconds; timeout=0 means no timeout, so this only fires with a positive timeout; a stalled download queue also causes it.

Common situations: CI scripts with tight timeouts waiting for large model downloads; stalled network downloads exceeding the caller's timeout; caller sets a short timeout for a multi-GB model.

Understand the failure class

Related errors


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