invoke-ai/InvokeAI · error · OSError
[Errno 17] File {job.download_path} exists
Error message
[Errno 17] File {job.download_path} exists What it means
On a fresh (non-resume, resume_from == 0) download, if the target file already exists at job.download_path the library refuses to clobber it and raises OSError mimicking errno 17 (EEXIST). This is deliberate: unlike the earlier size-check block, this branch only triggers when resume logic didn't already reconcile the existing file, protecting an existing complete file from being overwritten.
Source
Thrown at invokeai/app/services/download/download_default.py:571
# Existing file does not match expected size; treat as corrupt and restart.
self._logger.debug(
"Resume check: existing file size mismatch; deleting and restarting "
f"path={job.download_path} existing_size={existing_size} expected={job.total_bytes}"
)
job.download_path.unlink()
free_space = disk_usage(job.download_path.parent).free
GB = 2**30
remaining_bytes = max(job.total_bytes - job.bytes, 0)
if free_space < remaining_bytes:
raise RuntimeError(
f"Free disk space {free_space / GB:.2f} GB is not enough for download of {remaining_bytes / GB:.2f} GB."
)
# Don't clobber an existing file. See commit 82c2c85202f88c6d24ff84710f297cfc6ae174af
# for code that instead resumes an interrupted download.
if job.download_path.exists() and resume_from == 0:
raise OSError(f"[Errno 17] File {job.download_path} exists")
# append ".downloading" to the path
# signal caller that the download is starting. At this point, key fields such as
# download_path and total_bytes will be populated. We call it here because the might
# discover that the local file is already complete and generate a COMPLETED status.
self._signal_job_started(job)
expected_total = job.total_bytes or job.expected_total_bytes or content_length
# "range not satisfiable" - local file is at least as large as the remote file
if resp.status_code == 416 or (expected_total > 0 and job.bytes >= expected_total):
self._logger.info(f"{job.download_path}: complete file found. Skipping.")
return
# "partial content" - local file is smaller than remote file
elif resp.status_code == 206 or job.bytes > 0:
self._logger.info(f"{job.download_path}: partial file found. Resuming")
# some other errorView on GitHub (pinned to 0b6a024f2f)
Solutions
- Resume instead of re-submitting: call the queue's resume/join API so the existing file is reconciled (complete files are skipped).
- Delete or rename the existing file at job.download_path if you know it is stale, then re-submit.
- Check the existing file's size against the remote content-length — if equal, the download is already done; skip it.
- Choose a different dest path if the collision is unintended.
Example fix
// before: re-submitting always
queue.submit(source, dest)
// after: skip if already present and complete
if not dest.exists():
queue.submit(source, dest)
else:
logger.info('already downloaded: %s', dest) Defensive patterns
Strategy: validation
Validate before calling
import requests
from pathlib import Path
def already_downloaded(url: str, dest: Path) -> bool:
if not dest.exists():
return False
remote = int(requests.head(url, allow_redirects=True, timeout=10).headers.get('Content-Length', 0))
return remote > 0 and dest.stat().st_size == remote Try / catch
try:
queue.submit(source, dest)
except OSError as e:
if 'Errno 17' in str(e) or 'exists' in str(e):
logger.info('%s already present; skipping', dest) Prevention
- Check dest.exists() (ideally with size match) before submitting fresh downloads.
- Use the queue's resume API for existing files instead of re-submitting.
- Make install scripts idempotent: skip submissions whose target file already exists.
- Avoid multiple queue entries pointing at the same dest path.
When it happens
Trigger: Submitting a download whose explicit dest file path already exists on disk while resume_from == 0 — e.g. re-submitting the same job with cancel instead of resume, or a leftover file from a prior install.
Common situations: Re-running an install script that re-submits already-downloaded models; a prior download crashed leaving the final file in place; two queue entries pointing at the same dest; user copied the file manually before the queue finished.
Related errors
- Video file not found
- Free disk space {free_space / GB:.2f} GB is not enough for d
- Destination image already exists: {move.new_path}
- Destination thumbnail already exists: {move.new_thumbnail_pa
- Failed to delete video
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/b6e1f8ac7be4f7ff.
Report an issue: GitHub.