Repository: pypa/twine

Issue:
Error during upload. ValueError: 499 is not a valid HTTPStatus
### Is there an existing issue for this?

- [x] I have searched the existing issues (open and closed), and could not find an existing issue

### What keywords did you use to search existing issues?

499 
ValueError

### What operating system(s) are you using?

Linux

### If you selected 'Other', describe your Operating System here

_No response_

### What version of Python are you running?

```console
Python 3.12 (this is GHA hosted runner)
```

### How did you install twine? Did you use your operating system's package manager or pip or something else?

```console
https://github.com/karellen/karellen-llvm/actions/runs/23277186082/job/67682577805#step:3:21

pip install --no-input --user --break-system-packages twine
```

### What version of twine do you have installed (include the complete output)

```console
twine --version
twine version 6.2.0 (keyring: 25.7.0, packaging: 26.0, requests: 2.32.5,
requests-toolbelt: 1.0.0, urllib3: 2.6.3, id: 1.6.1)
```

### Which package repository are you using?

pypi.org

### Please describe the issue that you are experiencing

```
WARNING  Error during upload. Retry with the --verbose option for more details. 
Traceback (most recent call last):
  File "/home/runner/.local/lib/python3.12/site-packages/twine/__main__.py", line 33, in main
    error = cli.dispatch(sys.argv[1:])
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.local/lib/python3.12/site-packages/twine/cli.py", line 139, in dispatch
    return main(args.args)
           ^^^^^^^^^^^^^^^
  File "/home/runner/.local/lib/python3.12/site-packages/twine/commands/upload.py", line 253, in main
    return upload(upload_settings, parsed_args.dists)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.local/lib/python3.12/site-packages/twine/commands/upload.py", line 216, in upload
    utils.check_status_code(resp, upload_settings.verbose)
  File "/home/runner/.local/lib/python3.12/site-packages/twine/utils.py", line 248, in check_status_code
    raise err
  File "/home/runner/.local/lib/python3.12/site-packages/twine/utils.py", line 240, in check_status_code
    response.raise_for_status()
  File "/home/runner/.local/lib/python3.12/site-packages/requests/models.py", line 1026, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 499 Client Error: status code 499 for url: https://upload.pypi.org/legacy/

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/.local/bin/twine", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/runner/.local/lib/python3.12/site-packages/twine/__main__.py", line 40, in main
    status_phrase = http.HTTPStatus(status_code).phrase
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/enum.py", line 757, in __call__
    return cls.__new__(cls, value)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/enum.py", line 1171, in __new__
    raise ve_exc
ValueError: 499 is not a valid HTTPStatus
```

https://github.com/karellen/karellen-llvm/actions/runs/23277186082/job/67682577805#step:6:2129

### Please list the steps required to reproduce this behaviour

This is GHA Workflow failing, the full reproducer is here: https://github.com/karellen/karellen-llvm/actions/runs/23277186082/workflow#L58

### Please include the PKG-INFO file contents from the artifact you're attempting to upload

```email
Artifacts are publicly published and are contained in the release.
This is the artifact that is failing: https://github.com/karellen/karellen-llvm/releases/download/v22.1.1.post17/karellen_llvm_core_debug-22.1.1.post17-py3-none-manylinux_2_28_x86_64.whl
Other artifacts continue to succeed.
```

### A redacted version of your `.pypirc` file

```ini
there isn't one, it's CI host.
```

### Anything else you'd like to mention?

_No response_

Discussion hints:

Solution patch:
diff --git a/twine/__main__.py b/twine/__main__.py
index a0d29164..6e5486ed 100644
--- a/twine/__main__.py
+++ b/twine/__main__.py
@@ -37,7 +37,20 @@ def main() -> Any:
 
         error = True
         status_code = response.status_code
-        status_phrase = http.HTTPStatus(status_code).phrase
+
+        try:
+            status_phrase = http.HTTPStatus(status_code).phrase
+        except ValueError:
+            # HTTPStatus will raise ValueError if the server responds with
+            # a non-standard status code. This is almost certainly an upstream
+            # index error since non-standard status codes should only be used
+            # for internal signaling, but there's nothing we can do about that
+            # here other than avoid propagating it as an unhandled exception.
+            #
+            # See: <https://github.com/pypa/twine/issues/1304>
+            # See: <https://github.com/pypi/warehouse/issues/19713>
+            status_phrase = "Unknown Status"
+
         logger.error(
             f"{exc.__class__.__name__}: {status_code} {status_phrase} "
             f"from {response.url}\n"


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index c9e491d0..57ec8e14 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -13,6 +13,7 @@
 import sys
 
 import pretend
+import pytest
 import requests
 
 from twine import __main__ as dunder_main
@@ -42,7 +43,13 @@ def test_exception_handling(monkeypatch, capsys):
     )
 
 
-def test_http_exception_handling(monkeypatch, capsys):
+@pytest.mark.parametrize(
+    ("status_code", "status_phrase", "reason"),
+    [(400, "Bad Request", "Error reason"), (599, "Unknown Status", "Another reason")],
+)
+def test_http_exception_handling(
+    monkeypatch, capsys, status_code, status_phrase, reason
+):
     monkeypatch.setattr(sys, "argv", ["twine", "upload", "test.whl"])
     monkeypatch.setattr(
         upload,
@@ -50,9 +57,7 @@ def test_http_exception_handling(monkeypatch, capsys):
         pretend.raiser(
             requests.HTTPError(
                 response=pretend.stub(
-                    url="https://example.org",
-                    status_code=400,
-                    reason="Error reason",
+                    url="https://example.org", status_code=status_code, reason=reason
                 )
             )
         ),
@@ -64,8 +69,8 @@ def test_http_exception_handling(monkeypatch, capsys):
     captured = capsys.readouterr()
 
     assert _unwrap_lines(captured.out) == (
-        f"{RED_ERROR} HTTPError: 400 Bad Request from https://example.org "
-        "Error reason"
+        f"{RED_ERROR} HTTPError: {status_code} {status_phrase} "
+        f"from https://example.org {reason}"
     )