Successful subagent completion messages bypass the completion-context token limit

Open 💬 0 comments Opened Aug 25, 2026 by ipFirto

What issue are you seeing?

Component and source

Codex core multi-agent V1 and V2 on current main:

  • Commit: cbfd999db78cb088d2bd89b52051efe6f44555a4
  • Component: codex-rs/core and codex-rs/protocol
  • Platform/model/subscription: independent; the problem occurs before the parent model request is constructed

Actual behavior

Successful subagent completion messages are forwarded into the parent agent's model-visible context without any size limit.

Codex defines a 1,000-token completion-message budget in session_prefix.rs. However, only errored completions are truncated. AgentStatus::Completed(Some(message)) clones the entire final message unchanged:

In MultiAgent V2, this unbounded string is placed in InterAgentCommunication and delivered to the parent:

It is then copied unchanged into a model-visible ResponseItem::AgentMessage:

ContextManager::process_item truncates function and custom-tool outputs, but explicitly clones AgentMessage unchanged:

The legacy MultiAgent V1 path has the same problem because it serializes the complete AgentStatus into a SubagentNotification:

Impact

A subagent returning a sufficiently large final answer can inject a single item larger than the intended completion budget, and potentially larger than the parent's remaining context window. This can cause excessive context growth, premature compaction, or context_length_exceeded on the parent's next request.

The existing test covers truncation of errored completions only:

What steps can reproduce the bug?

This can be reproduced deterministically with a regression test beside the existing error-completion test:

~~~rust
#[test]
fn successful_completion_message_stays_below_completion_budget() {
let message = format_inter_agent_completion_message(
AgentPath::root(),
AgentPath::try_from("/root/worker").expect("valid agent path"),
&AgentStatus::Completed(Some("large subagent result ".repeat(20_000))),
)
.expect("completed status should produce a completion message");

assert!(
approx_token_count(&message) < COMPLETION_MESSAGE_MAX_TOKENS,
"successful completion contained {} tokens",
approx_token_count(&message),
);
}
~~~

The assertion currently fails because the complete successful result is returned unchanged.

A higher-level reproduction is:

  1. Start a parent task with multi-agent support enabled.
  2. Spawn a child agent whose final response exceeds 10,000 tokens.
  3. Wait for the child to complete.
  4. Inspect the parent's next model request or normalized conversation history.
  5. Observe that the entire child result is present in a single AgentMessage without truncation or spill handling.

What is the expected behavior?

All model-visible subagent completion messages should have a hard size limit.

At minimum:

  • Successful completions should honor COMPLETION_MESSAGE_MAX_TOKENS, just as error completions do.
  • The same limit should apply consistently to MultiAgent V1 and V2.
  • Truncated messages should contain an explicit marker explaining that content was omitted.
  • If the full result must remain accessible, the bounded parent message should reference the child thread or a stored artifact instead of embedding the entire result.
  • Integration coverage should verify that the actual message delivered to the parent remains bounded.

Additional information

This is related to the broader context-hygiene concern in #40493, which mentions bounding successful subagent completion forwarding.

This report is intentionally narrower and provides a deterministic source-level reproduction: a single successful completion bypasses the explicit 1,000-token completion-message budget. It does not depend on cumulative tool-output growth or recursive session-state reads.

A possible fix is to truncate or spill successful completion payloads before constructing either InterAgentCompletionMessage or SubagentNotification, while preserving a bounded reference to the child thread for retrieving the full result.

View original on GitHub ↗