Skip to content

Bedrock streaming: TypeError in _decode_tool_use when tool_use["input"] is already a dict #3827

@timelfrink

Description

@timelfrink

Describe your environment

  • OS: macOS / Linux (Docker)
  • Python Version: 3.12
  • Package Version: opentelemetry-instrumentation-botocore==0.54b1
  • AWS SDK: boto3 with Bedrock Runtime
  • Framework: LangChain with ChatBedrock

What happened?

When using OpenTelemetry instrumentation with AWS Bedrock's tool calling (streaming mode), the application crashes with a TypeError in bedrock_utils.py at the _decode_tool_use function.

The error occurs because tool_use["input"] is already a dict, but the code attempts to call json.loads() on it, which only accepts strings.

Steps to Reproduce

from langchain_aws import ChatBedrock
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool

@tool
def get_current_time() -> str:
    """Get the current time."""
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

llm = ChatBedrock(
    model_id="eu.anthropic.claude-3-7-sonnet-20250219-v1:0",
    region_name="eu-central-1",
    model_kwargs={"temperature": 0.1, "max_tokens": 2048}
)

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant with access to tools."),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, [get_current_time], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[get_current_time])

response = agent_executor.invoke({"input": "What time is it now?"})

Run with OpenTelemetry:

opentelemetry-instrument python script.py

Expected Result

The agent should successfully invoke the tool and return the result without errors.

Actual Result

TypeError: the JSON object must be str, bytes or bytearray, not dict

Full Traceback:

Traceback (most recent call last):
  File "/path/to/script.py", line 36, in <module>
    response = agent_executor.invoke({"input": "What time is it now?"})
  File "langchain/chains/base.py", line 165, in invoke
    self._call(inputs, run_manager=run_manager)
  File "langchain/agents/agent.py", line 1625, in _call
    next_step_output = self._take_next_step(
  File "langchain/agents/agent.py", line 1325, in _take_next_step
    list(
  File "langchain/agents/agent.py", line 1352, in _iter_next_step
    output = self._action_agent.plan(
  File "langchain/agents/agent.py", line 573, in plan
    for chunk in self.runnable.stream(inputs, config={"callbacks": callbacks}):
  File "langchain_core/runnables/base.py", line 3650, in stream
    yield from self.transform(iter([input]), config, **kwargs)
  File "langchain_core/runnables/base.py", line 3636, in transform
    yield from self._transform_stream_with_config(
  File "langchain_core/runnables/base.py", line 2372, in _transform_stream_with_config
    chunk: Output = context.run(next, iterator)
  File "langchain_core/runnables/base.py", line 3595, in _transform
    yield from final_pipeline
  File "langchain_core/runnables/base.py", line 1571, in transform
    for ichunk in input:
  File "langchain_core/runnables/base.py", line 5927, in transform
    yield from self.bound.transform(
  File "langchain_core/runnables/base.py", line 1589, in transform
    yield from self.stream(final, config, **kwargs)
  File "langchain_core/language_models/chat_models.py", line 522, in stream
    for chunk in self._stream(input_messages, stop=stop, **kwargs):
  File "langchain_aws/chat_models/bedrock.py", line 869, in _stream
    for chunk in self._prepare_input_and_invoke_stream(
  File "langchain_aws/llms/bedrock.py", line 1165, in _prepare_input_and_invoke_stream
    for chunk in LLMInputOutputAdapter.prepare_output_stream(
  File "langchain_aws/llms/bedrock.py", line 509, in prepare_output_stream
    for event in stream:
  File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 170, in __iter__
    self._process_event(event)
  File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 192, in _process_event
    self._process_anthropic_claude_chunk(chunk)
  File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 332, in _process_anthropic_claude_chunk
    _decode_tool_use(self._content_block)
  File "opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py", line 42, in _decode_tool_use
    tool_use["input"] = json.loads(tool_use["input"])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: the JSON object must be str, bytes or bytearray, not dict

Root Cause

In bedrock_utils.py, the _decode_tool_use function assumes tool_use["input"] is always a string:

def _decode_tool_use(tool_use):
    # input get sent encoded in json
    if "input" in tool_use:
        try:
            tool_use["input"] = json.loads(tool_use["input"])
        except json.JSONDecodeError:
            pass
    return tool_use

Problem: The function catches json.JSONDecodeError but not TypeError. When tool_use["input"] is already a dict (which happens with Bedrock's streaming tool calls), json.loads() raises TypeError, not JSONDecodeError.

Additional context

Would you like to implement a fix?

Yes

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

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