Skip to content

[BUG] AgentCoreCodeInterpreter's _create_tool_result stringifies binary content, breaking readFiles for binary files #370

@dipetkov

Description

@dipetkov

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

0.2.19

Tools Package Version

0.2.19

Tools used

AgentCoreCodeInterpreter

Python Version

3.11.9

Operating System

Amazon Linux 2

Installation Method

other

Steps to Reproduce

  1. Execute code in sandbox that generates a PNG (e.g., matplotlib chart)
  2. Call readFiles to retrieve it
  3. Binary data is corrupted

Expected Behavior

readFiles returns binary content (bytes) that can be used to recreate files locally (e.g., PNG images generated by matplotlib).

Actual Behavior

Binary content is wrapped in str(), returning a string like "b'\x89PNG\r\n...'" instead of actual bytes.

Additional Context

When reading binary files (e.g., PNG images) via readFiles, the _create_tool_result method wraps content in str(), destroying binary data.

Discovered while trying to generate matplotlib charts in the sandbox and retrieve the PNG files.

Location

src/strands_tools/code_interpreter/agent_core_code_interpreter.py, line ~495

Current behavior

return {
   "status": "success" if not is_error else "error",
   "content": [{"text": str(result.get("content"))}],  # Destroys binary
}

Binary content becomes a string like "b'\\x89PNG\\r\\n...'" instead of actual bytes.

Possible Solution

Patched the issue with:

    def _create_tool_result(self, response: Dict[str, Any]) -> Dict[str, Any]:
        """Create tool result from response without stringifying resource content."""
        if "stream" in response:
            event_stream = response["stream"]
            for event in event_stream:
                if "result" in event:
                    result = event["result"]
                    content = result.get("content")
                    is_error = response.get("isError", False)
                    # Preserve content structure instead of stringifying
                    if isinstance(content, list):
                        return {
                            "status": "success" if not is_error else "error",
                            "content": content,
                        }
                    # Fall back to text wrapping for non-list content
                    return {
                        "status": "success" if not is_error else "error",
                        "content": [{"text": str(content)}],
                    }
            return {
                "status": "error",
                "content": [{"text": f"Failed to create tool result: {response}"}],
            }
        return response

This worked for my use case.

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