Does PyPI's "File already exists" error mean my interrupted upload actually succeeded?
0 reputation · 15 Sept 2025, 08:53 UTC
0 reputation · 15 Sept 2025, 08:53 UTC
I'm publishing a package to PyPI with twine against the legacy upload endpoint, and a release workflow hit HTTPError: 400 Client Error: File already exists when retrying an upload of the same version after an earlier attempt appeared to fail mid-transfer. The project page does not list the file, yet the server refuses the re-upload.
My understanding is that PyPI treats a re-upload of byte-identical content as idempotent but rejects different content under the same filename, and that there is no public API exposing whether a given file was fully processed or left in some partial state. The web UI only shows files after successful processing, so maintainers seemingly cannot distinguish "the file is live and fine" from "a stale artifact is blocking me." I am assuming current PyPI (Warehouse) behavior as of 2026; I have not verified whether the newer JSON API changes any of this.
What exactly does the 400 response guarantee about the state of the previously uploaded file? Is there any supported way to confirm whether the existing file's hash matches my local artifact before deciding to bump the version? And if the earlier upload was genuinely interrupted, is a version bump the only sanctioned remediation?
31275 reputation · 15 Sept 2025, 16:52 UTC
No — the 400 does not tell you the earlier upload succeeded. It only guarantees that PyPI has already registered a file with that exact filename (project, version, and distribution tag) in its database. Whether the stored bytes are complete, corrupt, or simply different from what you're now sending is something the error alone cannot tell you.
PyPI's legacy upload endpoint historically treats a re-upload of byte-identical content under an existing filename as an idempotent no-op, while rejecting different content under the same filename with 400. So a 400 on retry suggests one of two things:
There is no supported API to inspect a partially processed upload, so you cannot directly distinguish these server-side. What you can do is check whether a fully processed file exists.
If the file was fully processed, it is served and its SHA-256 is published in the JSON API. Compare it against your local artifact:
sha256sum dist/mypkg-1.2.3-py3-none-any.whl
# then fetch:
# https://pypi.org/pypi/mypkg/1.2.3/json
# inspect urls[].filename and urls[].digests.sha256Decision rule:
For cases 2 and 3, effectively yes. PyPI does not allow replacing or deleting an uploaded file, and deleting the release in the web UI does not free the filename. The sanctioned remediation is a new version — and a post-release (1.2.3.post1) exists precisely for re-releasing without a code change. For case 3 specifically, you can also file an issue on the PyPI support tracker, but a post-release bump is usually faster.
This reflects well-established Warehouse behavior as of 2026; exact idempotency semantics of the legacy endpoint are version-sensitive and worth verifying against current PyPI documentation before relying on them in automation. The JSON API digest check above is the most reliable confirmation available today.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.