{"record":{"id":"0a562215c790f8c0","repo":"invoke-ai/InvokeAI","slug":"download-interrupted-resume-required","errorCode":null,"errorMessage":"Download interrupted. Resume required.","messagePattern":"Download interrupted\\. Resume required\\.","errorType":"exception","errorClass":"DownloadJobCancelledException","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/download/download_default.py","lineNumber":619,"sourceCode":"        report_delta = job.total_bytes / 100  # report every 1% change\n        last_report_bytes = 0\n\n        # DOWNLOAD LOOP\n        with open(in_progress_path, open_mode) as file:\n            for data in resp.iter_content(chunk_size=DOWNLOAD_CHUNK_SIZE):\n                if job.cancelled:\n                    raise DownloadJobCancelledException(\"Job was cancelled at caller's request\")\n\n                job.bytes += file.write(data)\n                if (job.bytes - last_report_bytes >= report_delta) or (job.bytes >= job.total_bytes):\n                    last_report_bytes = job.bytes\n                    self._signal_job_progress(job)\n\n        if job.total_bytes > 0 and job.bytes < job.total_bytes:\n            job.resume_required = True\n            job.resume_message = \"Download interrupted. Resume required.\"\n            job.pause()\n            raise DownloadJobCancelledException(\"Download interrupted. Resume required.\")\n\n        # if we get here we are done and can rename the file to the original dest\n        self._logger.debug(f\"{job.source}: saved to {job.download_path} (bytes={job.bytes})\")\n        in_progress_path.rename(job.download_path)\n\n    def _validate_url(self, url: str) -> None:\n        \"\"\"Refuse to fetch URLs that point at addresses only the server can reach.\"\"\"\n        validate_download_url(str(url), allow_private_urls=self._app_config.allow_private_download_urls)\n\n    def _reject_unsafe_redirect(self, response: requests.Response, *args: Any, **kwargs: Any) -> requests.Response:\n        \"\"\"Response hook: vet each redirect target before `requests` follows it.\"\"\"\n        if response.is_redirect or response.is_permanent_redirect:\n            location = response.headers.get(\"location\")\n            if location:\n                try:\n                    self._validate_url(urljoin(response.url, location))\n                except Exception:\n                    response.close()","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/download/download_default.py#L601-L637","documentation":"DownloadJobCancelledException raised by _do_download in InvokeAI's download service when a download loop ends before all expected bytes were written (job.bytes < job.total_bytes). The service flags the job with resume_required=True so the partial file can be resumed rather than restarted, pauses the job, and cancels the current download attempt. It signals an interrupted network transfer, not a logic error.","triggerScenarios":"A network connection drops or the read stream ends prematurely mid-download while job.total_bytes > 0 and fewer bytes than expected were written in _do_download (invoked via _download_next_item).","commonSituations":"Flaky Wi-Fi or VPN drops during model downloads; remote host (e.g. HuggingFace/CDN) closes the connection early; proxy timeouts; long downloads on unstable connections; sleeping laptops mid-transfer.","solutions":["Retry the download; the job is marked resume_required so the service resumes from job.bytes instead of restarting","Check network stability / disable VPN or proxy and retry","Delete the partial in-progress file if resume repeatedly fails, forcing a clean restart","Check server-side availability of the source URL (may be a truncated/failed response from the host)"],"exampleFix":"// before: restarting the whole download on any failure\ndownload_and_install(source)\n\n// after: let InvokeAI resume the partial download\ntry:\n    download_and_install(source)\nexcept DownloadJobCancelledException:\n    # job.resume_required is True; re-enqueue the job to resume from job.bytes\n    queue_download(job.source, resume=True)","handlingStrategy":"retry","validationCode":"None","typeGuard":"def is_resume_required(job) -> bool:\n    return getattr(job, \"resume_required\", False) and job.total_bytes > 0 and job.bytes < job.total_bytes","tryCatchPattern":"try:\n    service.download(job)\nexcept DownloadJobCancelledException as e:\n    if \"Resume required\" in str(e):\n        requeue_job_with_resume(job)  # resumes from job.bytes\n    else:\n        raise","preventionTips":["Use a stable wired connection or reliable network for large model downloads","Ensure the process isn't suspended (laptop sleep) mid-download","Keep enough disk space so partial files aren't discarded","Prefer resumable download sources (HTTP range support)"],"tags":["network","download","resume","interrupted-transfer"],"backgroundTag":"download-interrupted-resume-required","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}