Skip to content

Commit 55b9ecc

Browse files
Guard transport error extraction against broken containers
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 7b517dd commit 55b9ecc

2 files changed

Lines changed: 62 additions & 11 deletions

File tree

hyperbrowser/transport/error_utils.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,26 +136,36 @@ def _stringify_error_value(value: Any, *, _depth: int = 0) -> str:
136136
return value
137137
if isinstance(value, dict):
138138
for key in ("message", "error", "detail", "errors", "msg", "title", "reason"):
139-
nested_value = value.get(key)
139+
try:
140+
nested_value = value.get(key)
141+
except Exception:
142+
continue
140143
if nested_value is not None:
141144
return _stringify_error_value(nested_value, _depth=_depth + 1)
142145
if isinstance(value, (list, tuple)):
143146
max_list_items = 10
144-
collected_messages = [
145-
item_message
146-
for item_message in (
147-
_stringify_error_value(item, _depth=_depth + 1)
148-
for item in value[:max_list_items]
149-
)
150-
if item_message
151-
]
147+
try:
148+
list_items = value[:max_list_items]
149+
except Exception:
150+
return _safe_to_string(value)
151+
collected_messages = []
152+
try:
153+
for item in list_items:
154+
item_message = _stringify_error_value(item, _depth=_depth + 1)
155+
if item_message:
156+
collected_messages.append(item_message)
157+
except Exception:
158+
return _safe_to_string(value)
152159
if collected_messages:
153160
joined_messages = (
154161
collected_messages[0]
155162
if len(collected_messages) == 1
156163
else "; ".join(collected_messages)
157164
)
158-
remaining_items = len(value) - max_list_items
165+
try:
166+
remaining_items = len(value) - max_list_items
167+
except Exception:
168+
return joined_messages
159169
if remaining_items > 0:
160170
return f"{joined_messages}; ... (+{remaining_items} more)"
161171
return joined_messages
@@ -183,7 +193,10 @@ def _fallback_message() -> str:
183193
extracted_message: str
184194
if isinstance(error_data, dict):
185195
for key in ("message", "error", "detail", "errors", "title", "reason"):
186-
message = error_data.get(key)
196+
try:
197+
message = error_data.get(key)
198+
except Exception:
199+
continue
187200
if message is not None:
188201
candidate_message = _stringify_error_value(message)
189202
if candidate_message.strip():

tests/test_transport_error_utils.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,26 @@ def json(self):
167167
raise ValueError("invalid json")
168168

169169

170+
class _BrokenGetErrorDict(dict):
171+
def get(self, key, default=None):
172+
_ = key
173+
_ = default
174+
raise RuntimeError("broken dict get")
175+
176+
def __str__(self) -> str:
177+
return "broken-get-error-dict"
178+
179+
180+
class _BrokenSliceErrorList(list):
181+
def __getitem__(self, key):
182+
if isinstance(key, slice):
183+
raise RuntimeError("broken slice")
184+
return super().__getitem__(key)
185+
186+
def __str__(self) -> str:
187+
return "broken-slice-error-list"
188+
189+
170190
def test_extract_request_error_context_uses_unknown_when_request_unset():
171191
method, url = extract_request_error_context(httpx.RequestError("network down"))
172192

@@ -664,6 +684,24 @@ def test_extract_error_message_handles_unstringifiable_message_values():
664684
assert message == "<unstringifiable _UnstringifiableErrorValue>"
665685

666686

687+
def test_extract_error_message_handles_dict_get_failures():
688+
message = extract_error_message(
689+
_DummyResponse({"message": _BrokenGetErrorDict({"inner": object()})}),
690+
RuntimeError("fallback detail"),
691+
)
692+
693+
assert message == "broken-get-error-dict"
694+
695+
696+
def test_extract_error_message_handles_list_slice_failures():
697+
message = extract_error_message(
698+
_DummyResponse({"errors": _BrokenSliceErrorList([{"msg": "issue-1"}])}),
699+
RuntimeError("fallback detail"),
700+
)
701+
702+
assert message == "broken-slice-error-list"
703+
704+
667705
def test_extract_error_message_uses_fallback_for_blank_dict_message():
668706
message = extract_error_message(
669707
_DummyResponse({"message": " "}), RuntimeError("fallback detail")

0 commit comments

Comments
 (0)