22from typing import Optional , Any
33
44
5+ def _safe_exception_text (value : Any , * , fallback : str ) -> str :
6+ try :
7+ text_value = str (value )
8+ except Exception :
9+ return fallback
10+ sanitized_value = "" .join (
11+ "?" if ord (character ) < 32 or ord (character ) == 127 else character
12+ for character in text_value
13+ )
14+ if sanitized_value .strip ():
15+ return sanitized_value
16+ return fallback
17+
18+
519class HyperbrowserError (Exception ):
620 """Base exception class for Hyperbrowser SDK errors"""
721
@@ -19,17 +33,29 @@ def __init__(
1933
2034 def __str__ (self ) -> str :
2135 """Custom string representation to show a cleaner error message"""
22- parts = [f"{ self .args [0 ]} " ]
36+ message_value = self .args [0 ] if self .args else "Hyperbrowser error"
37+ message_text = _safe_exception_text (
38+ message_value ,
39+ fallback = "Hyperbrowser error" ,
40+ )
41+ parts = [message_text ]
2342
2443 if self .status_code is not None :
25- parts .append (f"Status: { self .status_code } " )
44+ status_text = _safe_exception_text (
45+ self .status_code ,
46+ fallback = "<unknown status>" ,
47+ )
48+ parts .append (f"Status: { status_text } " )
2649
2750 if self .original_error and not isinstance (
2851 self .original_error , HyperbrowserError
2952 ):
3053 error_type = type (self .original_error ).__name__
31- error_msg = str (self .original_error )
32- if error_msg and error_msg != str (self .args [0 ]):
54+ error_msg = _safe_exception_text (
55+ self .original_error ,
56+ fallback = f"<unstringifiable { error_type } >" ,
57+ )
58+ if error_msg != message_text :
3359 parts .append (f"Caused by { error_type } : { error_msg } " )
3460
3561 return " - " .join (parts )
0 commit comments