invoke-ai/InvokeAI · warning · DownloadJobCancelledException

Resume refused by server. Restart required.

Error message

Resume refused by server. Restart required.

What it means

During a resume attempt, the server responded 416 (Range Not Satisfiable) but the local partial file's byte offset did not exactly equal the remote file's total size, so the library cannot trust the partial data. The job is marked resume_required and paused, and DownloadJobCancelledException is raised to force the download to restart from scratch rather than produce a corrupt file. It is a control-flow signal to the download queue, not a fatal failure of the file itself.

Source

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

            # Range not satisfiable - local partial is already complete
            match = re.fullmatch(r"bytes \*/(\d+)", resp.headers.get("Content-Range", ""), flags=re.IGNORECASE)
            # Content-Range is optional on 416 responses. Reuse the size known when
            # the download started, but never resume_from itself: that would accept
            # every partial file as complete.
            expected = int(match.group(1)) if match else (job.expected_total_bytes or job.total_bytes or None)
            if expected is not None and resume_from == expected:
                job.total_bytes = expected
                job.expected_total_bytes = expected
                job.bytes = resume_from
                job.download_path = job.download_path or job.dest
                self._in_progress_path(job.download_path).rename(job.download_path)
                self._signal_job_started(job)
                self._signal_job_complete(job)
                return
            job.resume_required = True
            job.resume_message = "Resume refused by server. Restart required."
            job.pause()
            raise DownloadJobCancelledException("Resume refused by server. Restart required.")
        if not resp.ok:
            host = urlparse(str(resp.url or url)).netloc
            status = resp.status_code
            reason = resp.reason
            if status >= 500:
                self._logger.error(f"Remote server error from {host}: HTTP {status} {reason}")
                raise HTTPError(reason)
            self._logger.error(f"Download failed from {host}: HTTP {status} {reason}")
            raise HTTPError(reason)

        job.content_type = resp.headers.get("Content-Type")
        job.etag = resp.headers.get("ETag") or job.etag
        job.last_modified = resp.headers.get("Last-Modified") or job.last_modified
        content_length = int(resp.headers.get("content-length", 0))

        if job.dest.is_dir():
            parsed_url = urlparse(str(url))
            file_name = os.path.basename(parsed_url.path)  # default is to use the last bit of the URL

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Delete the stale partial file (the `.downloading` file in the destination directory) and resubmit the download so it restarts from byte 0.
  2. Verify the remote URL still points at the same file version (check ETag/Last-Modified or file size); pin a specific revision if the host is a model registry.
  3. Check any proxy/CDN in front of the host that may answer 416 with a mismatched Content-Range; bypass it or disable resume.
  4. Catch DownloadJobCancelledException and inspect job.resume_message to distinguish this from a caller cancellation, then restart the job programmatically.

Example fix

// before: blindly retrying the job, which keeps failing with 416
queue.resume(job.id)

// after: discard the partial file, then restart cleanly
from pathlib import Path
partial = Path(job.local_path).with_suffix('.downloading')
if partial.exists():
    partial.unlink()
queue.cancel(job.id)
queue.submit(job.source, job.dest)
Defensive patterns

Strategy: retry

Validate before calling

import requests
head = requests.head(url, allow_redirects=True)
remote_size = int(head.headers.get('Content-Length', 0))
partial = dest_path.with_suffix('.downloading')
if partial.exists() and remote_size and partial.stat().st_size > remote_size:
    partial.unlink()  # stale/inconsistent partial, remove before resuming

Try / catch

try:
    queue.resume(job.id)
except DownloadJobCancelledException as e:
    if 'Restart required' in str(e):
        partial = Path(job.local_path).with_suffix('.downloading')
        partial.unlink(missing_ok=True)
        queue.cancel(job.id)
        queue.submit(job.source, job.dest)

Prevention

When it happens

Trigger: A previously interrupted download is resumed (_do_download with resume_from > 0) and the server returns HTTP 416 while the Content-Range total (or job.expected_total_bytes/job.total_bytes) does not match resume_from — e.g. the remote file changed size or was replaced between sessions.

Common situations: Mirror/HuggingFace file was updated after the first download attempt; a proxy or CDN returns 416 with inconsistent Content-Range; the local .downloading file was truncated or corrupted externally while the server thinks the range is unsatisfiable.

Related errors


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