Skip to content

Commit c0a618b

Browse files
Improve transport error extraction for nested payload fields
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent b6073cc commit c0a618b

2 files changed

Lines changed: 47 additions & 6 deletions

File tree

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,35 @@
1+
import json
12
from typing import Any
23

34
import httpx
45

56

7+
def _stringify_error_value(value: Any) -> str:
8+
if isinstance(value, str):
9+
return value
10+
if isinstance(value, dict):
11+
for key in ("message", "error", "detail"):
12+
nested_value = value.get(key)
13+
if nested_value is not None:
14+
return _stringify_error_value(nested_value)
15+
try:
16+
return json.dumps(value, sort_keys=True)
17+
except TypeError:
18+
return str(value)
19+
20+
621
def extract_error_message(response: httpx.Response, fallback_error: Exception) -> str:
722
try:
823
error_data: Any = response.json()
924
except Exception:
1025
return response.text or str(fallback_error)
1126

1227
if isinstance(error_data, dict):
13-
message = error_data.get("message") or error_data.get("error")
14-
if message is not None:
15-
return str(message)
28+
for key in ("message", "error", "detail"):
29+
message = error_data.get(key)
30+
if message is not None:
31+
return _stringify_error_value(message)
1632
return response.text or str(fallback_error)
1733
if isinstance(error_data, str):
1834
return error_data
19-
return str(response.text or str(fallback_error))
35+
return _stringify_error_value(response.text or str(fallback_error))

tests/test_transport_response_handling.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_sync_handle_response_with_non_string_message_field_coerces_to_string():
9797
try:
9898
response = _build_response(500, '{"message":{"detail":"failed"}}')
9999

100-
with pytest.raises(HyperbrowserError, match="detail"):
100+
with pytest.raises(HyperbrowserError, match="failed"):
101101
transport._handle_response(response)
102102
finally:
103103
transport.close()
@@ -109,7 +109,32 @@ async def run() -> None:
109109
try:
110110
response = _build_response(500, '{"message":{"detail":"failed"}}')
111111

112-
with pytest.raises(HyperbrowserError, match="detail"):
112+
with pytest.raises(HyperbrowserError, match="failed"):
113+
await transport._handle_response(response)
114+
finally:
115+
await transport.close()
116+
117+
asyncio.run(run())
118+
119+
120+
def test_sync_handle_response_with_nested_error_message_uses_nested_value():
121+
transport = SyncTransport(api_key="test-key")
122+
try:
123+
response = _build_response(500, '{"error":{"message":"nested failure"}}')
124+
125+
with pytest.raises(HyperbrowserError, match="nested failure"):
126+
transport._handle_response(response)
127+
finally:
128+
transport.close()
129+
130+
131+
def test_async_handle_response_with_detail_field_uses_detail_value():
132+
async def run() -> None:
133+
transport = AsyncTransport(api_key="test-key")
134+
try:
135+
response = _build_response(500, '{"detail":"invalid request"}')
136+
137+
with pytest.raises(HyperbrowserError, match="invalid request"):
113138
await transport._handle_response(response)
114139
finally:
115140
await transport.close()

0 commit comments

Comments
 (0)