Skip to content

fix: merge stray top-level system kwarg into messages array in Chat Completions transport (#60821) - #60891

Closed
kyssta-exe wants to merge 1 commit into
NousResearch:mainfrom
kyssta-exe:fix/60821-system
Closed

fix: merge stray top-level system kwarg into messages array in Chat Completions transport (#60821)#60891
kyssta-exe wants to merge 1 commit into
NousResearch:mainfrom
kyssta-exe:fix/60821-system

Conversation

@kyssta-exe

Copy link
Copy Markdown
Contributor

When request_overrides or a provider profile accidentally forwards system as a top-level OpenAI Chat Completions kwarg, merge it into the messages array instead of crashing.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openrouter OpenRouter aggregator P2 Medium — degraded but workaround exists labels Jul 8, 2026

@falkoro falkoro 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.

Community review — verified locally. Direction is correct: a top-level system kwarg is invalid for Chat Completions and merging it into the messages array is the right recovery. Also checked the mutation concern: sanitized comes from convert_messages(), which deep-copies per call, so the in-place insert(0, …) is safe.

Three things worth tightening:

  1. The guard elif k == "system" and sanitized: means an empty messages list still falls through to api_kwargs["system"] = v — the original 400 in that edge. sanitized.insert(0, …) works fine on an empty list, so the and sanitized can drop (with the exact-match branch guarded by if sanitized and sanitized[0].get("role") == "system").
  2. System message content may be a content-parts list rather than a string on some providers; (list or "") + "\n" raises TypeError. An isinstance(content, str) check (falling back to inserting a separate system message) covers it.
  3. No tests — this is exactly the kind of fix a small unit test pins cheaply (overrides with system + existing system message / no system message / empty messages).

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Comment (token read-only)

PR 60891 merges a stray top-level system kwarg into the messages array in Chat Completions transport. Fixes a protocol handling edge case. Well-scoped (1 file, 14 additions). No security issues or debug artifacts detected.

LGTM - awaiting maintainer approval.

@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 the narrowly scoped defensive fix. It needs rework before it can reliably address the reported failure.

Problems

  • The confirmed #60821 injector was llm_request middleware. Current main builds transport kwargs at agent/conversation_loop.py:1161, then middleware replaces them at agent/conversation_loop.py:1175-1192; this PR only handles request_overrides, so it does not intercept that path.
  • The added and sanitized guard leaves messages=[] on the existing fallback that forwards system into api_kwargs (agent/transports/chat_completions.py:601-608).
  • The merge expression assumes string content. Existing system messages may use content parts (tests/providers/test_provider_profiles.py:471), for which list-plus-string raises TypeError.

Suggested changes

  • Re-scope the reported fix to the post-middleware boundary or present this as separate request_overrides hardening.
  • Handle empty messages and content-part system messages without forwarding a top-level system kwarg.
  • Add regression tests in tests/agent/transports/test_chat_completions.py for text, absent, empty, and content-parts system-message cases.

Automated hermes-sweeper review.

for k, v in overrides.items():
if k == "extra_body" and isinstance(v, dict):
extra_body.update(v)
elif k == "system" and sanitized:

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.

and sanitized leaves an empty messages list on the final else, which still forwards the invalid top-level system kwarg. Handle the empty-list case by inserting a system message too.

if sanitized[0].get("role") == "system":
sanitized[0] = {
**sanitized[0],
"content": (sanitized[0].get("content") or "")

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.

System content can be a non-empty content-parts list (tests/providers/test_provider_profiles.py:471); adding "\n" to it raises TypeError. Merge only strings, or insert a separate system message for non-string content.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 10, 2026
@kyssta-exe

Copy link
Copy Markdown
Contributor Author

Stale — no merge activity for 4-6 days. Can resubmit if still needed.

@kyssta-exe kyssta-exe closed this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/openrouter OpenRouter aggregator sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants