Skip to content

[BUG] Strands Agent incorrectly sets SpanKind for Agent Spans #1044

@liustve

Description

@liustve

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.12.0

Python Version

3.12.2

Operating System

macOS

Installation Method

pip

Steps to Reproduce

  1. Basic strands agent deployed on Bedrock AgentCore runtime with ADOT Python dependency:
import uvicorn
import time
import boto3
import json
from datetime import datetime
from strands import Agent, tool
from strands.models import BedrockModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from bedrock_agentcore_starter_toolkit.operations.memory.manager import MemoryManager
from bedrock_agentcore.memory.session import MemorySessionManager
from bedrock_agentcore.memory.constants import ConversationalMessage, MessageRole
from bedrock_agentcore_starter_toolkit.operations.memory.models.strategies import SemanticStrategy

@tool
def get_clinic_hours():
    """Get pet clinic operating hours"""
    return "Monday-Friday: 8AM-6PM, Saturday: 9AM-4PM, Sunday: Closed."

agent_app = BedrockAgentCoreApp()

# Initialize memory
memory_manager = MemoryManager(region_name="us-east-1")
memory = memory_manager.get_or_create_memory(
    name="PetClinicMemory",
    description="Pet clinic conversation memory",
    strategies=[SemanticStrategy(name="clinicMemory", description="Pet clinic semantic memory", namespaces=["/clinic/{actorId}"])]
)

# Wait for memory to be active
while True:
    memory_status = memory_manager.get_memory(memory.get("id"))
    if memory_status.get("status") == "ACTIVE":
        break
    time.sleep(1)

session_manager = MemorySessionManager(memory_id=memory.get("id"), region_name="us-east-1")

def create_clinic_agent():
    model = BedrockModel(model_id="us.anthropic.claude-3-5-haiku-20241022-v1:0")
    return Agent(model=model, tools=[get_clinic_hours], system_prompt="You are a pet clinic assistant.")

@agent_app.entrypoint
async def invoke(payload, context):
    agent = create_clinic_agent()
    msg = payload.get('prompt', '')
    
    response_data = []
    async for event in agent.stream_async(msg):
        if 'data' in event:
            response_data.append(event['data'])
    
    response = ''.join(response_data)
    
    # Store in memory using direct boto3 CreateEvent call
    client = boto3.client('bedrock-agentcore', region_name='us-east-1')
    
    # Create user message event
    client.create_event(
        memoryId="PetClinicMemory-VC8Jp09Rb1",
        actorId="user",
        sessionId="clinic_session",
        eventTimestamp=int(datetime.utcnow().timestamp()),
        payload=[
            {
                "conversational": {
                    "content": {
                        "text": msg
                    },
                    "role": "USER"
                }
            }
        ]
    )
    
    # Create assistant message event
    client.create_event(
        memoryId="PetClinicMemory-VC8Jp09Rb1",
        actorId="user",
        sessionId="clinic_session",
        eventTimestamp=int(datetime.utcnow().timestamp()),
        payload=[
            {
                "conversational": {
                    "content": {
                        "text": response
                    },
                    "role": "ASSISTANT"
                }
            }
        ]
    )
    
    return response

if __name__ == "__main__":    
    uvicorn.run(agent_app, host='0.0.0.0', port=8080)
  1. requirements.txt:
strands-agents
strands-agents-tools
uv
boto3
bedrock-agentcore
bedrock-agentcore-starter-toolkit
aws-opentelemetry-distro>=0.12.1
  1. Dockerfile:
FROM public.ecr.aws/docker/library/python:3.12-slim
WORKDIR /app



COPY requirements.txt requirements.txt
# Install from requirements file
RUN pip install -r requirements.txt




RUN pip install aws-opentelemetry-distro>=0.10.1


# Set AWS region environment variable

ENV AWS_REGION=us-east-1
ENV AWS_DEFAULT_REGION=us-east-1


# Signal that this is running in Docker for host binding logic
ENV DOCKER_CONTAINER=1

# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore

EXPOSE 8080
EXPOSE 8000

# Copy entire project (respecting .dockerignore)
COPY . .

# Use the full module path

CMD ["opentelemetry-instrument", "python", "-m", "app"]

Expected Behavior

We see a broken Trace view for Strands Agent on CloudWatch/GenAI Obs Console below. There’s an unexpected node named strands-agents that always appears in traces for agents built with Strands SDK. This happens because Strands SDK manually creates an OTel Client Span when starting the agentic workflow for incoming prompts. In OTel spec, Client Spans represent for outbound HTTP calls, but in this case, it should be an Internal Span to capture the event loop activity.

Actual Behavior

Per OTel's documentation:

Span kind SHOULD be CLIENTand MAY be set to INTERNAL on spans representing call to models running in the same process. It’s RECOMMENDED to use CLIENT kind when the GenAI system being instrumented usually runs in a different process than its client or when the GenAI call happens over instrumented protocol such as HTTP.

In this case, the agent span represents internal activity. Labeling it as a CLIENT span would not align with OTel’s intended use of that span kind.

Additional Context

Image

Possible Solution

No response

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