twine upload "400 Bad Request ... File already exists" in CI
PyPI refuses to accept a file for a version it already has, because releases are immutable. Either the version was not bumped, or a re-run is re-uploading the same artifacts.
What this error means
twine upload fails with "HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/" and "File already exists. See https://pypi.org/help/#file-name-reuse for more information."
Uploading my_pkg-1.2.0-py3-none-any.whl
HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/
File already exists. See https://pypi.org/help/#file-name-reuse for more information.Common causes
The version was already released
PyPI never lets you re-upload or replace a file for an existing version. Publishing the same version again triggers this 400.
A re-run of a release that partially or fully uploaded
A retried workflow uploads dist files that already landed on a previous run, so PyPI rejects the duplicate filenames.
How to fix it
Bump the version and rebuild
- Increment the version in your project metadata.
- Rebuild the distributions so filenames carry the new version.
- Upload the fresh artifacts.
python -m build
twine upload dist/*Make re-runs idempotent with --skip-existing
Tell twine to skip files that already exist so a retried release does not fail on duplicates.
twine upload --skip-existing dist/*How to prevent it
- Publish only from a tagged release with a fresh version.
- Use
--skip-existingso retried release jobs do not fail. - Clean the dist/ directory before building to avoid stale artifacts.