fix(blocks): SendWebRequestBlock to properly handle HTTP error responses (#9655)

### Issue
The SendWebRequestBlock currently fails to properly route HTTP error
responses (4xx, 5xx) to their designated output pins (`client_error` and
`server_error`). Instead, these errors are being sent to the default
"Error" pin, breaking expected workflows that depend on proper error
handling.

### Root Cause
The underlying issue is that our custom `requests` module from
`backend.util.request` appears to automatically raise exceptions for
error status codes (similar to how `raise_for_status()` works in the
standard requests library). When these exceptions are thrown, the
block's conditional logic for handling different status codes is
bypassed entirely.

### Changes
This PR adds proper exception handling to catch HTTP errors raised by
the requests module and routes them to the appropriate output pins:
- Added a try-except block to capture `requests.exceptions.HTTPError`
- Extract status code and response data from the caught exception
- Yield to the proper pin based on the status code (4xx → client_error,
5xx → server_error)
- Maintain consistent behavior with the original design intent

### Additional Context
This change maintains backward compatibility while ensuring the block
behaves according to its documented functionality. Users can now
properly handle 4xx and 5xx errors in their workflows as originally
intended.

<!-- Clearly explain the need for these changes: -->
### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  <!-- Put your test plan here: -->
- [x] Test the block with new changes and old and ensure expected
behavior

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
This commit is contained in:
Toran Bruce Richards
2025-03-19 13:05:07 +00:00
committed by GitHub
parent dc1d5faa5d
commit 6d36be6f86

View File

@@ -1,11 +1,16 @@
import json
import logging
from enum import Enum
from typing import Any
from requests.exceptions import HTTPError, RequestException
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
from backend.util.request import requests
logger = logging.getLogger(name=__name__)
class HttpMethod(Enum):
GET = "GET"
@@ -43,8 +48,9 @@ class SendWebRequestBlock(Block):
class Output(BlockSchema):
response: object = SchemaField(description="The response from the server")
client_error: object = SchemaField(description="The error on 4xx status codes")
server_error: object = SchemaField(description="The error on 5xx status codes")
client_error: object = SchemaField(description="Errors on 4xx status codes")
server_error: object = SchemaField(description="Errors on 5xx status codes")
error: str = SchemaField(description="Errors for all other exceptions")
def __init__(self):
super().__init__(
@@ -68,20 +74,40 @@ class SendWebRequestBlock(Block):
# we should send it as plain text instead
input_data.json_format = False
response = requests.request(
input_data.method.value,
input_data.url,
headers=input_data.headers,
json=body if input_data.json_format else None,
data=body if not input_data.json_format else None,
)
result = response.json() if input_data.json_format else response.text
if response.status_code // 100 == 2:
try:
response = requests.request(
input_data.method.value,
input_data.url,
headers=input_data.headers,
json=body if input_data.json_format else None,
data=body if not input_data.json_format else None,
)
result = response.json() if input_data.json_format else response.text
yield "response", result
elif response.status_code // 100 == 4:
yield "client_error", result
elif response.status_code // 100 == 5:
yield "server_error", result
else:
raise ValueError(f"Unexpected status code: {response.status_code}")
except HTTPError as e:
# Handle error responses
try:
result = e.response.json() if input_data.json_format else str(e)
except json.JSONDecodeError:
result = str(e)
if 400 <= e.response.status_code < 500:
yield "client_error", result
elif 500 <= e.response.status_code < 600:
yield "server_error", result
else:
error_msg = (
"Unexpected status code "
f"{e.response.status_code} '{e.response.reason}'"
)
logger.warning(error_msg)
yield "error", error_msg
except RequestException as e:
# Handle other request-related exceptions
yield "error", str(e)
except Exception as e:
# Catch any other unexpected exceptions
yield "error", str(e)