Skip to content

fix(gateway): guard chained .get() against None intermediate values - #34695

Closed
annguyenNous wants to merge 1 commit into
NousResearch:mainfrom
annguyenNous:fix/chained-get-none-bypass
Closed

annguyenNous wants to merge 1 commit into
NousResearch:mainfrom
annguyenNous:fix/chained-get-none-bypass

Conversation

@annguyenNous

Copy link
Copy Markdown
Contributor

Summary

.get("key", {}) only applies the default {} when the key is absent from the dict. When the key exists with value None (null in JSON/API responses), .get() returns None and the subsequent .get() raises AttributeError.

This is the same pattern documented in Pitfall 28.get(key, default) vs .get(key) or default.

Fix

Replace .get("key", {}).get(...) with (.get("key") or {}).get(...) which handles both missing keys AND None values.

Affected locations (8 instances, 6 files)

File Line Context Severity
gateway/run.py 11629 tool_call function name for TTS filter Low-Med
acp_adapter/server.py 1700-1701 tool name/description extraction Low-Med
gateway/platforms/qqbot/onboard.py 103 API response task_id extraction Low-Med
gateway/platforms/yuanbao.py 2022, 2041 message content parsing (x2) Low-Med
gateway/platforms/slack.py 2434, 2535 block text extraction (x2) Low
tui_gateway/server.py 3360 error message extraction Low

Impact

All instances involve parsing external API responses or tool call structures where null/None values are possible. The crash manifests as AttributeError: 'NoneType' object has no attribute 'get'.

.get("key", {}) only applies the default when the key is ABSENT.
When the key exists with value None (null in JSON), .get() returns
None and the subsequent .get() raises AttributeError.

Fix: replace .get("key", {}).get(...) with (.get("key") or {}).get(...)
which handles both missing keys AND None values.

8 instances across 6 files:
- gateway/run.py: tool_call function name check
- acp_adapter/server.py: tool name/description extraction
- gateway/platforms/qqbot/onboard.py: API response task_id
- gateway/platforms/yuanbao.py: message content parsing (x2)
- gateway/platforms/slack.py: block text extraction (x2)
- tui_gateway/server.py: error message extraction
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/acp Agent Communication Protocol adapter comp/tui Terminal UI (ui-tui/ + tui_gateway/) platform/qqbot QQ Bot adapter platform/slack Slack app adapter labels May 29, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for identifying a real defensive-parsing issue. The premise still holds on current main, but this branch needs targeted salvage before it can provide the stated coverage.

Problems

  • The Slack edits target gateway/platforms/slack.py, but the live handlers moved to plugins/platforms/slack/adapter.py in 5600105478ffde29d7566b45421b100eaa29c4ef; the unsafe chains remain at plugins/platforms/slack/adapter.py:3514 and :3636.
  • The Yuanbao methods remain unsafe at gateway/platforms/yuanbao.py:2156 and :2175 on current main.
  • ACP has a sibling unsafe access over the same tool list at acp_adapter/server.py:1800, in addition to the two display accesses changed by this PR.
  • The six-file diff contains no regression test for present-but-null intermediates.

Suggested changes

  • Port the Slack and Yuanbao guards to their current locations, guard the ACP sibling, and add focused null-intermediate regression tests.

Automated hermes-sweeper review.

for block in message.get("blocks", []):
if block.get("type") == "section":
original_text = block.get("text", {}).get("text", "")
original_text = (block.get("text") or {}).get("text", "")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This handler has moved on current main: please port this guard to plugins/platforms/slack/adapter.py:3514 (and the matching approval handler at :3636), otherwise the active Slack code remains unguarded.

Comment thread acp_adapter/server.py
for t in tools:
name = t.get("function", {}).get("name", "?")
desc = t.get("function", {}).get("description", "")
name = (t.get("function") or {}).get("name", "?")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please also guard the sibling tool.get("function", {}).get("name") used to build valid_tool_names; on current main it is in the same _cmd_tools flow at acp_adapter/server.py:1800.

@teknium1 teknium1 added 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
@teknium1

Copy link
Copy Markdown
Collaborator

Merged via #70196 — your commit was cherry-picked/reapplied onto current main with your authorship preserved in git history: your None-intermediate guards were cherry-picked.

Thanks for the contribution!

@teknium1 teknium1 closed this Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/acp Agent Communication Protocol adapter comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have platform/qqbot QQ Bot adapter platform/slack Slack app adapter 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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants