Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions dynamiq/nodes/tools/stagehand.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,25 @@ def _upload_file_bytes_to_steel_browser_session(self, session_id: str, file_byte
files = {"file": (file_name, io.BytesIO(file_bytes))}
response = requests.post(url, headers=self._get_steel_browser_headers(), files=files, timeout=self.timeout)
response.raise_for_status()
return response.json()
try:
return response.json()
except ValueError as e:
logger.error(f"Failed to parse JSON from Steel Browser upload response: {e}")
raise ToolExecutionException(
f"Steel Browser API returned invalid JSON after file upload: {e}",
recoverable=True,
)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider chaining the caught JSON parsing error when re-raising ToolExecutionException (e.g., ... from e). Without exception chaining, the original traceback/context for why response.json() failed is lost, which makes debugging proxy/HTML/empty-body cases harder.

Suggested change
)
) from e

Copilot uses AI. Check for mistakes.

def _list_steel_browser_session_files(self, session_id: str) -> list[dict]:
"""List all files in the Steel Browser session's filesystem."""
url = f"{self._get_steel_browser_base_url()}/sessions/{session_id}/files"
response = requests.get(url, headers=self._get_steel_browser_headers(), timeout=self.timeout)
response.raise_for_status()
payload = response.json()
try:
payload = response.json()
except ValueError as e:
logger.error(f"Failed to parse JSON from Steel Browser files response: {e}")
return []
if isinstance(payload, dict):
logger.info(f"Steel session files: {payload.get('data') or []}")
return payload.get("data") or []
Expand Down
7 changes: 5 additions & 2 deletions dynamiq/nodes/tools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ def guess_mime_type_from_bytes(data: bytes, filename: str = None) -> str:
elif data.startswith(b"<?xml"):
return "application/xml"
elif data.startswith(b"{") or data.startswith(b"["):
json.loads(data.decode("utf-8"))
return "application/json"
try:
json.loads(data.decode("utf-8"))
return "application/json"
except (json.JSONDecodeError, UnicodeDecodeError):
pass
Comment on lines 76 to +81
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit coverage for the new JSON-parse failure path. guess_mime_type_from_bytes now swallows JSONDecodeError/UnicodeDecodeError and falls through to the other heuristics; there are already unit tests for dynamiq.nodes.tools.utils (currently only for sanitize_filename), but nothing asserts MIME detection behavior for invalid/valid JSON payloads.

Copilot uses AI. Check for mistakes.
elif data.startswith(b"Name,") or b"," in data[:100] and b"\n" in data[:100]:
return "text/csv"
elif data.startswith(b"#") or (b" " in data and b"\n" in data and len(data.split(b"\n")) > 1):
Expand Down
Loading