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."
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.
- run: twine upload dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}Match the token to the right index
- Generate the token on the same index you upload to (PyPI vs TestPyPI are separate).
- Use a project-scoped token for the exact project, or an account-scoped token.
- Confirm the token has not expired or been revoked.
twine upload --repository testpypi dist/*How to prevent it
- Always upload with username
__token__and apypi-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.