Skip to content

fix(gateway): default request_overrides to {} to avoid NoneType crash - #7291

Closed
xiaoyuervae wants to merge 1 commit into
NousResearch:mainfrom
xiaoyuervae:fix/gateway-request-overrides-none
Closed

fix(gateway): default request_overrides to {} to avoid NoneType crash#7291
xiaoyuervae wants to merge 1 commit into
NousResearch:mainfrom
xiaoyuervae:fix/gateway-request-overrides-none

Conversation

@xiaoyuervae

Copy link
Copy Markdown

Summary

The gateway's per-turn agent reuse path assigns agent.request_overrides = turn_route.get(\"request_overrides\") at gateway/run.py:6974. dict.get() returns None when the key is absent, so request_overrides becomes None on the running agent. The next turn crashes inside _build_api_kwargs at:

fast_mode=self.request_overrides.get(\"speed\") == \"fast\",

with AttributeError: 'NoneType' object has no attribute 'get'.

The retry loop classifies this as a non-retryable API error and fast-fails into the fallback provider chain, so in practice every gateway turn silently falls back from the primary model to the first fallback (e.g. `openai-codex`) without ever issuing an HTTP request to the primary provider. The primary model is never actually tried.

Root cause

All 7 other call sites pass request_overrides through the AIAgent constructor, which normalizes the value via dict(request_overrides or {}) at run_agent.py:678. This direct attribute-assignment branch in gateway/run.py is the only path that bypassed that normalization.

Fix

One-line change — mirror the constructor's defensive default:

-            agent.request_overrides = turn_route.get(\"request_overrides\")
+            agent.request_overrides = turn_route.get(\"request_overrides\") or {}

Reproduction (before the fix)

  1. Run hermes --profile <p> gateway with a primary provider that does not set `request_overrides` in its turn routing (e.g. a basic Discord DM flow through a custom provider).
  2. Send a message. Gateway log shows:
    Fallback activated: <primary_model> → <fallback_model> (<fallback_provider>)
    
    with no HTTP request logged for the primary model and no `request_dump_*.json` written for the primary call.
  3. Enabling DEBUG + a traceback in the error handler at `run_agent.py:8296` reveals:
    File \"run_agent.py\", line 5522, in _build_api_kwargs
        fast_mode=self.request_overrides.get(\"speed\") == \"fast\",
    AttributeError: 'NoneType' object has no attribute 'get'
    

After applying the fix, the primary model is invoked normally.

Test plan

  • Manually verified on a `wechat-investment` profile with a custom Anthropic-Messages provider — primary model now handles Discord DMs correctly instead of fast-failing to the fallback.
  • No existing tests covered the direct-attribute-assignment path; happy to add a regression test if maintainers would like one (simplest shape would assert that `_build_api_kwargs` works after `agent.request_overrides = None`).

When the gateway reuses a cached agent across turns, it directly assigns
`agent.request_overrides = turn_route.get("request_overrides")`.
`dict.get()` returns `None` when the key is absent, so `request_overrides`
becomes `None` on the agent instance. Every subsequent turn then fails
inside `_build_api_kwargs` at:

    fast_mode=self.request_overrides.get("speed") == "fast",

raising `AttributeError: 'NoneType' object has no attribute 'get'`, which
the retry loop classifies as a non-retryable API error and fast-fails into
the fallback provider chain. In practice this manifests as every Discord
DM silently falling back from the primary model to `openai-codex` with no
HTTP request ever leaving the process — the primary model was never
actually tried.

All other call sites pass `request_overrides` through the `AIAgent`
constructor, which normalizes via `dict(request_overrides or {})` in
`run_agent.py:678`. This direct attribute assignment in `gateway/run.py`
is the only path that bypassed that normalization.

Fix: mirror the constructor's defensive default by wrapping the get() in
`or {}`.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the detailed write-up and repro steps, @xiaoyuervae! This crash was fixed on main the same day this PR was opened.

Automated hermes-sweeper review.

  • The AttributeError: 'NoneType' object has no attribute 'get' in _build_api_kwargs was addressed by commit 0bea60351 (PR fix: handle NoneType request_overrides in fast_mode check #7350), which added the (self.request_overrides or {}).get("speed") guard in run_agent.py.
  • That fix shipped in v2026.4.13 and is on current main at run_agent.py:7481.
  • Your PR's approach (fixing the assignment in gateway/run.py:9964) is arguably cleaner since it addresses the root cause rather than the symptom. If you'd like to reopen with a revised scope, feel free — but the crash itself is resolved.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants