From 1c6974be38c819d39237df3224e0c88ee289541d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Braun Date: Sat, 23 May 2026 10:21:51 +0200 Subject: [PATCH] Wrap urllib3 LocationParseError raised from create_connection in InvalidURL When a hostname has labels that fail validation (e.g. a label longer than 63 characters, as constrained by RFC 1035), prepare_url's existing handler catches LocationParseError at parse time and surfaces it as InvalidURL. The same exception can also be raised later from urllib3's util.connection.create_connection, where it bubbles up through HTTPAdapter.send. The (_SSLError, _HTTPError) except clause in send catches it (LocationParseError is a urllib3.HTTPError) but the final `else: raise` re-raises it bare, so callers see a raw urllib3 exception instead of a requests-native one. Add an explicit branch that converts LocationValueError (parent of LocationParseError) to InvalidURL, mirroring the behaviour of get_connection_with_tls_context earlier in send. Regression test: parametrize test_errors with a 64-character DNS label and assert that requests.exceptions.InvalidURL is raised. Fixes #5744 --- src/requests/adapters.py | 11 +++++++++++ tests/test_requests.py | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/requests/adapters.py b/src/requests/adapters.py index 40fe8a6d5a..b5d0f0a65b 100644 --- a/src/requests/adapters.py +++ b/src/requests/adapters.py @@ -742,6 +742,17 @@ def send( raise ReadTimeout(e, request=request) elif isinstance(e, _InvalidHeader): raise InvalidHeader(e, request=request) + elif isinstance(e, LocationValueError): + # urllib3 may raise LocationParseError (subclass of + # LocationValueError) from inside `create_connection` when the + # hostname has labels that fail validation (e.g. a label longer + # than 63 characters). prepare_url catches this at parse time, + # but the same exception can also be raised later from the + # socket layer where it would otherwise bubble up to the caller + # as a urllib3 exception. Wrap it in InvalidURL so users see a + # requests-native exception, matching the behaviour of + # get_connection_with_tls_context above. See GH #5744. + raise InvalidURL(e, request=request) else: raise diff --git a/tests/test_requests.py b/tests/test_requests.py index 571535fe79..c00f24c81f 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -588,6 +588,13 @@ def test_basicauth_encodes_byte_strings(self): ("http://localhost:1", ConnectionError), # Inputting a URL that cannot be parsed should raise an InvalidURL error ("http://fe80::5054:ff:fe5a:fc0", InvalidURL), + # A DNS label longer than 63 chars survives prepare_url but is + # rejected by urllib3 inside create_connection. Should surface as + # InvalidURL, not as a raw urllib3.LocationParseError. GH #5744. + ( + "http://" + ("a" * 64) + ".example.com", + InvalidURL, + ), ), ) def test_errors(self, url, exception):