Publish and release conflicts — EPUBLISHCONFLICT / File already exists
Log signatures
You cannot publish over the previously published versions EPUBLISHCONFLICT HTTPError: 400 File already exists crate version 1.2.3 is already uploaded a release with the same tag already exists
What’s happening
Two causes cover nearly everything: version-already-exists (EPUBLISHCONFLICT, PyPI's "File already exists", crates.io's "already uploaded", GitHub's "already_exists" on a tag) — most registries are immutable by design; you can never republish a version, even to fix it. This fires when a release job re-runs after a partial failure (half the artifacts published, half didn't) or when the version wasn't bumped. And auth failures (ENEEDAUTH, E403, 403 Forbidden) — the publish credential is missing, expired, or lacks rights to this package name; PyPI in particular returns 403 (not 404) for "name taken by someone else."
Fix playbook
- 1Determine which artifacts of the release actually made it (check the registry directly). Partial publishes are the norm in this failure class.
- 2Version conflict: bump the version — even for a one-character fix. Fighting registry immutability is a losing game, and it exists so that a given version is forever the same bytes for everyone.
- 3Re-run only the publish step, with already-published artifacts skipped (twine upload --skip-existing, conditional steps per artifact). Re-running the whole release pipeline re-builds and can produce different bytes.
- 4Auth: verify which token the step actually uses (env var name, trusted-publisher config) and its scopes/expiry. Don't print it while debugging.
- 5Tag conflicts: decide whether the existing tag is the release (then skip creation) or a stale tag (then delete deliberately, knowing consumers may have fetched it).
Prevention
- Make releases idempotent — every publish step either skips-if-exists or is conditional on a registry check. A safely re-runnable release pipeline turns this whole class into a non-event.
Triage every red build, not just this one.
This page is one of 31 failure classes the open-source patchrail CLI matches from a raw log — locally, with secrets redacted first. If a log stumps it, open an issue; if it saves you a debugging morning, a star helps other maintainers find it.
pipx install patchrail