Skip to content

fix: guard json.loads against non-JSON HTTP responses - #30138

Open
annguyenNous wants to merge 1 commit into
NousResearch:mainfrom
annguyenNous:fix/json-loads-guards
Open

fix: guard json.loads against non-JSON HTTP responses#30138
annguyenNous wants to merge 1 commit into
NousResearch:mainfrom
annguyenNous:fix/json-loads-guards

Conversation

@annguyenNous

Copy link
Copy Markdown
Contributor

Problem

Multiple tools and gateway adapters parse HTTP responses with json.loads() without catching json.JSONDecodeError. When servers return non-JSON bodies (HTML error pages, 502 gateways, empty responses), these crash with unhandled JSONDecodeError.

Fix

Add json.JSONDecodeError guards to 5 files across tools/ and gateway/:

File Issue Fix
tools/osv_check.py:151 OSV API response — no try/except Wrap in try/except, return [] on invalid JSON
tools/discord_tool.py:90 Discord API — only catches HTTPError Add JSONDecodeError to except clause
tools/browser_cdp_tool.py:143,177 Chrome CDP WebSocket frames — bare json.loads Wrap in try/except, continue on malformed frames
gateway/platforms/api_server.py:377 SQLite response cache — bare json.loads + possible None row[0] Add None check + try/except, return None on corrupt data
gateway/platforms/weixin.py:386,406 iLink API helpers — bare json.loads Wrap in try/except, raise RuntimeError with context

Tests

  • All 14 modified files pass py_compile syntax check
  • No behavioral changes for valid JSON responses — guards only activate on malformed input

Multiple tools and gateway adapters parse HTTP responses with
json.loads() without catching json.JSONDecodeError. When servers
return non-JSON bodies (HTML error pages, 502 gateways, empty
responses), these crash with unhandled JSONDecodeError.

Changes:
- tools/osv_check.py: wrap OSV API response parsing in try/except
- tools/discord_tool.py: add JSONDecodeError to existing except clause
- tools/browser_cdp_tool.py: guard WebSocket frame parsing (2 sites)
- gateway/platforms/api_server.py: guard SQLite response cache read,
  also add None check for row[0]
- gateway/platforms/weixin.py: guard _api_post and _api_get helpers

All guards return empty/fallback values or raise RuntimeError with
context, matching existing error-handling patterns in each file.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for covering several real malformed-response paths. Four targeted parsers remain unguarded on current main: tools/osv_check.py:165, tools/discord_tool.py:116, tools/browser_cdp_tool.py:237,271, and gateway/platforms/weixin.py:389,413.

Problems

  • gateway/platforms/api_server.py:459-471 already has a stronger corrupt-cache implementation: it catches the decode failure, logs it, evicts the poisoned entry, and returns None. The PR's stale hunk should not replace it.
  • tools/osv_check.py:46-51 already logs fail-open query failures. Returning [] from the parser would keep the allow behavior but suppress that diagnostic.
  • No malformed-response regression tests are included. Existing Discord and Weixin tests cover valid and HTTP-error responses, not invalid successful bodies.

Suggested changes

  • Salvage the four still-live guards, omit the ResponseStore hunk, preserve OSV diagnostics, and add targeted malformed-JSON tests for each path.

Automated hermes-sweeper review.

self._conn.commit()
return json.loads(row[0])
try:
return json.loads(row[0])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Current main already handles this more robustly at gateway/platforms/api_server.py:459-471: it logs and deletes the corrupt cache row before returning None. Please omit this stale hunk during salvage so a poisoned entry is not retained.

Comment thread tools/osv_check.py
raw = resp.read()
try:
result = json.loads(raw)
except (json.JSONDecodeError, ValueError):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

check_package_for_malware() currently catches this failure and logs a debug fail-open reason at tools/osv_check.py:46-51. Returning [] here preserves the allow result but loses that diagnostic; log invalid JSON here or propagate a contextual exception instead.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/browser Browser automation (CDP, Playwright) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants