Skip to content

[BUG] Swarm tool silently drops all sub-agent output #437

@jnzs1836

Description

@jnzs1836

Checks

  • I have updated to the lastest minor and patch version of Strands
  • I have checked the documentation and this is not expected behavior
  • I have searched ./issues and there are no duplicates of my issue

Strands Version

1.33.0

Tools Package Version

0.3.0

Tools used

swarm

Python Version

3.13.5

Operating System

macOS 15.6.1 (Darwin 24.6.0, arm64)

Installation Method

pip

Steps to Reproduce

Install the strands and strands-tools packages properly and run the following script:

from strands import Agent
from strands_tools import swarm

# 1. Create a parent agent with the swarm tool
orchestrator = Agent(tools=[swarm])

# 2. Run a swarm with a sub-agent that produces known output
orchestrator("Use swarm to create a single agent named 'test_agent' with "
             "system prompt 'Reply with exactly: SWARM_OUTPUT_MARKER_98765'. "
             "Task: 'Say the marker phrase.'")

# 3. Inspect the swarm tool result returned to the parent agent
# The "Individual Agent Contributions" section is empty despite
# the sub-agent producing output (visible in token counts)

Alternatively, inspect the source code directly:

  1. Open src/strands_tools/swarm.py
  2. Look at lines 451 and 466
  3. Both lines check hasattr(node_result.result, "content") — but AgentResult has no .content attribute
  4. The actual field is .message (a dict with a "content" key)
  5. Because hasattr() returns False, the entire extraction block is silently skipped

Expected Behavior

The parent agent should receive the full text output from all sub-agents in the swarm tool result. The "Individual Agent Contributions" section should contain each sub-agent's response text, and the "Final Team Result" section should contain the last agent's output.

Example expected output:

🎯 **Custom Agent Team Execution Complete**
📊 **Status:** Status.COMPLETED
⏱️ **Execution Time:** 2343ms

**🤖 Individual Agent Contributions:**

**TEST AGENT:**
SWARM_OUTPUT_MARKER_98765          ← sub-agent output should appear here

**🎯 Final Team Result:**
SWARM_OUTPUT_MARKER_98765          ← and here

**📈 Team Resource Usage:**
• Output tokens: 23

### Actual Behavior

The parent agent receives only execution metadata. All sub-agent text output is silently dropped. The "Individual Agent Contributions" section is empty:

🎯 Custom Agent Team Execution Complete
📊 Status: Status.COMPLETED
⏱️ Execution Time: 2343ms

🤖 Individual Agent Contributions:
← empty — content silently dropped

📈 Team Resource Usage:
• Input tokens: 559
• Output tokens: 23 ← sub-agent DID produce output (23 tokens)
• Total tokens: 582


No error or warning is logged. The sub-agent runs correctly and produces output (reflected in token counts), but the text never reaches the parent agent's context.

### Additional Context

### Root Cause

The swarm tool accesses `AgentResult.content` (which doesn't exist) instead of `AgentResult.message["content"]` (which contains the actual response). The `AgentResult` dataclass is defined in `strands/agent/agent_result.py`:

```python
@dataclass
class AgentResult:
    stop_reason: StopReason
    message: Message        # {"role": "assistant", "content": [...]}
    metrics: EventLoopMetrics
    state: Any
    # No .content attribute

The buggy code at lines 451 and 466:

if hasattr(node_result, "result") and hasattr(node_result.result, "content"):
    for content_block in node_result.result.content:
        if hasattr(content_block, "text") and content_block.text:
            agent_content.append(content_block.text)

Because hasattr(node_result.result, "content") returns False, the extraction block is silently skipped.

Why This Went Unnoticed

  1. Silent failurehasattr() returns False without any error or warning
  2. Token counts still appear — output tokens suggest the sub-agent worked
  3. LLMs compensate — parent agents fabricate plausible sub-agent findings from context clues
  4. No configuration workaround — no parameter controls result formatting

Impact

Affects every user of the swarm tool. The SDK's Swarm class, Graph pattern, and Agent-as-Tool pattern are NOT affected — only the tool wrapper in strands_tools.swarm.

Pattern Reference

The correct pattern already exists in workflow.py (lines 446-452):

content = (
    result.message.get("content", [])
    if hasattr(result.message, "get")
    else getattr(result.message, "content", [])
)

Possible Solution

Replace .content with .message and use dict-style access at both locations (lines 451 and 466):

if hasattr(node_result, "result") and hasattr(node_result.result, "message"):
    agent_content = []
    message = node_result.result.message
    content_blocks = message.get("content", []) if hasattr(message, "get") else []
    for content_block in content_blocks:
        if isinstance(content_block, dict) and content_block.get("text"):
            agent_content.append(content_block["text"])

This aligns with the AgentResult dataclass structure and follows the pattern already used in workflow.py. Test mocks in tests/test_swarm.py also need updating to use .message instead of .content.

Related Issues

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions