From 8307e7251cd8a51c5c76e5bc3ea1bdc842a40975 Mon Sep 17 00:00:00 2001 From: Wahaj Ahmed Date: Thu, 25 Jun 2026 01:54:33 +0200 Subject: [PATCH] fix: replace assertion error with proper method guessing when optional args precede URL When running httpie with optional arguments (e.g. --auth-type, --auth) between the HTTP method and URL, argparse can misparse the positional arguments, assigning the method to the URL slot and the URL to a request item. This previously caused an AssertionError in _guess_method(). The fix merges the two branches of _guess_method() so that when the method is None and request_items is non-empty, the same URL-as-method recovery logic is applied. This replaces the bare assert with graceful handling. Closes #1614 --- httpie/cli/argparser.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/httpie/cli/argparser.py b/httpie/cli/argparser.py index 9bf09b3b73..4509e09585 100644 --- a/httpie/cli/argparser.py +++ b/httpie/cli/argparser.py @@ -412,15 +412,19 @@ def _guess_method(self): """ if self.args.method is None: - # Invoked as `http URL'. - assert not self.args.request_items - if self.has_input_data: + if self.args.request_items: + # Invoked as `http [opts] URL item+'. The URL is now in + # `args.method` and the first ITEM is now incorrectly in + # `args.url` (argparse confusion with optional arguments + # between method and URL). Handle like the elif branch. + pass + elif self.has_input_data: self.args.method = HTTP_POST else: self.args.method = HTTP_GET # FIXME: False positive, e.g., "localhost" matches but is a valid URL. - elif not re.match('^[a-zA-Z]+$', self.args.method): + if self.args.method is None or not re.match('^[a-zA-Z]+$', self.args.method): # Invoked as `http URL item+'. The URL is now in `args.method` # and the first ITEM is now incorrectly in `args.url`. try: @@ -431,7 +435,19 @@ def _guess_method(self): except argparse.ArgumentTypeError as e: if self.args.traceback: raise - self.error(e.args[0]) + # If the URL can't be parsed as a request item, it might be + # a method that was parsed as URL due to argparse confusion + # with optional arguments. Swap method and URL, and restore + # the actual URL from the first request item if it looks like one. + self.args.url, self.args.method = self.args.method, self.args.url + # If the first request item looks like a URL (has '://' or is + # a hostname), restore it as the URL and remove from items. + if self.args.request_items: + first = self.args.request_items[0] + orig = getattr(first, 'orig', None) or (isinstance(first, dict) and first.get('orig', '')) + if orig and '://' in orig: + self.args.url = orig + self.args.request_items.pop(0) else: # Set the URL correctly