Skip to content

Commit f19b349

Browse files
author
SentienceDEV
committed
keep server side error/exception
1 parent 1c5df17 commit f19b349

File tree

1 file changed

+166
-15
lines changed

1 file changed

+166
-15
lines changed

sentience/snapshot.py

Lines changed: 166 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,143 @@
2020
MAX_PAYLOAD_BYTES = 10 * 1024 * 1024
2121

2222

23+
class SnapshotGatewayError(RuntimeError):
24+
"""
25+
Structured error for server-side (gateway) snapshot failures.
26+
27+
Keeps HTTP status/URL/response details available to callers for better logging/debugging.
28+
Subclasses RuntimeError for backward compatibility.
29+
"""
30+
31+
def __init__(
32+
self,
33+
message: str,
34+
*,
35+
status_code: int | None = None,
36+
url: str | None = None,
37+
request_id: str | None = None,
38+
response_text: str | None = None,
39+
cause: Exception | None = None,
40+
) -> None:
41+
super().__init__(message)
42+
self.status_code = status_code
43+
self.url = url
44+
self.request_id = request_id
45+
self.response_text = response_text
46+
# Note: callers should use `raise ... from cause` to preserve chaining.
47+
_ = cause
48+
49+
@staticmethod
50+
def _snip(s: str | None, n: int = 400) -> str | None:
51+
if not s:
52+
return None
53+
t = str(s).replace("\n", " ").replace("\r", " ").strip()
54+
return t[:n]
55+
56+
@classmethod
57+
def from_httpx(cls, e: Exception) -> "SnapshotGatewayError":
58+
status_code = None
59+
url = None
60+
request_id = None
61+
body = None
62+
try:
63+
resp = getattr(e, "response", None)
64+
if resp is not None:
65+
status_code = getattr(resp, "status_code", None)
66+
try:
67+
url = str(getattr(resp, "url", None) or "")
68+
except Exception:
69+
url = None
70+
try:
71+
headers = getattr(resp, "headers", None) or {}
72+
request_id = headers.get("x-request-id") or headers.get("x-trace-id")
73+
except Exception:
74+
request_id = None
75+
try:
76+
body = getattr(resp, "text", None)
77+
except Exception:
78+
body = None
79+
req = getattr(e, "request", None)
80+
if url is None and req is not None:
81+
try:
82+
url = str(getattr(req, "url", None) or "")
83+
except Exception:
84+
url = None
85+
except Exception:
86+
pass
87+
88+
msg = "Server-side snapshot API failed"
89+
bits = []
90+
if status_code is not None:
91+
bits.append(f"status={status_code}")
92+
if url:
93+
bits.append(f"url={url}")
94+
if request_id:
95+
bits.append(f"request_id={request_id}")
96+
body_snip = cls._snip(body)
97+
if body_snip:
98+
bits.append(f"body={body_snip}")
99+
if bits:
100+
msg = f"{msg}: " + " ".join(bits)
101+
msg = msg + ". Try using use_api=False to use local extension instead."
102+
return cls(
103+
msg,
104+
status_code=int(status_code) if status_code is not None else None,
105+
url=url,
106+
request_id=str(request_id) if request_id else None,
107+
response_text=body_snip,
108+
cause=e,
109+
)
110+
111+
@classmethod
112+
def from_requests(cls, e: Exception) -> "SnapshotGatewayError":
113+
status_code = None
114+
url = None
115+
request_id = None
116+
body = None
117+
try:
118+
resp = getattr(e, "response", None)
119+
if resp is not None:
120+
status_code = getattr(resp, "status_code", None)
121+
try:
122+
url = str(getattr(resp, "url", None) or "")
123+
except Exception:
124+
url = None
125+
try:
126+
headers = getattr(resp, "headers", None) or {}
127+
request_id = headers.get("x-request-id") or headers.get("x-trace-id")
128+
except Exception:
129+
request_id = None
130+
try:
131+
body = getattr(resp, "text", None)
132+
except Exception:
133+
body = None
134+
except Exception:
135+
pass
136+
msg = "Server-side snapshot API failed"
137+
bits = []
138+
if status_code is not None:
139+
bits.append(f"status={status_code}")
140+
if url:
141+
bits.append(f"url={url}")
142+
if request_id:
143+
bits.append(f"request_id={request_id}")
144+
body_snip = cls._snip(body)
145+
if body_snip:
146+
bits.append(f"body={body_snip}")
147+
if bits:
148+
msg = f"{msg}: " + " ".join(bits)
149+
msg = msg + ". Try using use_api=False to use local extension instead."
150+
return cls(
151+
msg,
152+
status_code=int(status_code) if status_code is not None else None,
153+
url=url,
154+
request_id=str(request_id) if request_id else None,
155+
response_text=body_snip,
156+
cause=e,
157+
)
158+
159+
23160
def _is_execution_context_destroyed_error(e: Exception) -> bool:
24161
"""
25162
Playwright can throw while a navigation is in-flight, invalidating the JS execution context.
@@ -170,14 +307,20 @@ def _post_snapshot_to_gateway_sync(
170307
"Content-Type": "application/json",
171308
}
172309

173-
response = requests.post(
174-
f"{api_url}/v1/snapshot",
175-
data=payload_json,
176-
headers=headers,
177-
timeout=30,
178-
)
179-
response.raise_for_status()
180-
return response.json()
310+
try:
311+
response = requests.post(
312+
f"{api_url}/v1/snapshot",
313+
data=payload_json,
314+
headers=headers,
315+
timeout=30,
316+
)
317+
response.raise_for_status()
318+
return response.json()
319+
except requests.exceptions.HTTPError as e:
320+
raise SnapshotGatewayError.from_requests(e) from e
321+
except requests.exceptions.RequestException as e:
322+
# Network/timeouts/etc (no status code available)
323+
raise SnapshotGatewayError.from_requests(e) from e
181324

182325

183326
async def _post_snapshot_to_gateway_async(
@@ -202,13 +345,21 @@ async def _post_snapshot_to_gateway_async(
202345
}
203346

204347
async with httpx.AsyncClient(timeout=30.0) as client:
205-
response = await client.post(
206-
f"{api_url}/v1/snapshot",
207-
content=payload_json,
208-
headers=headers,
209-
)
210-
response.raise_for_status()
211-
return response.json()
348+
try:
349+
response = await client.post(
350+
f"{api_url}/v1/snapshot",
351+
content=payload_json,
352+
headers=headers,
353+
)
354+
response.raise_for_status()
355+
return response.json()
356+
except httpx.HTTPStatusError as e:
357+
raise SnapshotGatewayError.from_httpx(e) from e
358+
except httpx.RequestError as e:
359+
raise SnapshotGatewayError.from_httpx(e) from e
360+
except Exception as e:
361+
# JSON decode or other unexpected issues — keep details if possible.
362+
raise SnapshotGatewayError.from_httpx(e) from e
212363

213364

214365
def _merge_api_result_with_local(

0 commit comments

Comments
 (0)