Skip to content
Latchkey

twine upload "HTTPError: 403 Forbidden ... invalid or non-existent authentication" in CI

PyPI received your upload but the credentials were wrong or missing. Since password logins were disabled, you must authenticate with the literal username __token__ and an API token as the password.

What this error means

twine upload fails with "HTTPError: 403 Forbidden from https://upload.pypi.org/legacy/" and "Invalid or non-existent authentication information. See https://pypi.org/help/#invalid-auth for more information."

twine
Uploading distributions to https://upload.pypi.org/legacy/
HTTPError: 403 Forbidden from https://upload.pypi.org/legacy/
Invalid or non-existent authentication information. See https://pypi.org/help/#invalid-auth for more information.

Common causes

Username/password used instead of an API token

PyPI no longer accepts account passwords for uploads. The username must be the literal __token__ and the password must be a pypi- prefixed API token.

A wrong, expired, or wrong-scope token

A token from TestPyPI used against PyPI, an expired token, or a project-scoped token for a different project all return 403.

How to fix it

Authenticate with __token__ and an API token

Pass the token via TWINE_USERNAME and TWINE_PASSWORD so twine sends the correct credentials.

.github/workflows/release.yml
- run: twine upload dist/*
  env:
    TWINE_USERNAME: __token__
    TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

Match the token to the right index

  1. Generate the token on the same index you upload to (PyPI vs TestPyPI are separate).
  2. Use a project-scoped token for the exact project, or an account-scoped token.
  3. Confirm the token has not expired or been revoked.
Terminal
twine upload --repository testpypi dist/*

How to prevent it

  • Always upload with username __token__ and a pypi- API token.
  • Keep separate tokens for PyPI and TestPyPI; do not cross them.
  • Scope tokens to a single project and store them as CI secrets.

Related guides

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