Skip to content
Latchkey

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."

twine
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

  1. Increment the version in your project metadata.
  2. Rebuild the distributions so filenames carry the new version.
  3. Upload the fresh artifacts.
Terminal
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.

Terminal
twine upload --skip-existing dist/*

How to prevent it

  • Publish only from a tagged release with a fresh version.
  • Use --skip-existing so retried release jobs do not fail.
  • Clean the dist/ directory before building to avoid stale artifacts.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →