Skip to content

fix(streaming): log warning when tool input JSON is malformed#2054

Open
Ratansairohith wants to merge 2 commits intostrands-agents:mainfrom
Ratansairohith:fix/malformed-tool-json-logging
Open

fix(streaming): log warning when tool input JSON is malformed#2054
Ratansairohith wants to merge 2 commits intostrands-agents:mainfrom
Ratansairohith:fix/malformed-tool-json-logging

Conversation

@Ratansairohith
Copy link
Copy Markdown
Contributor

Fixes #2051

Changes

src/strands/event_loop/streaming.py:

  • Added logger.warning() in the except ValueError block (line 282) that logs the tool name and raw input string when JSON parsing fails. The fallback to {} is preserved.

tests/strands/event_loop/test_streaming.py:

  • Added a test case for malformed tool input JSON to verify the code path is covered.

Test plan

  • All 106 existing streaming tests pass
  • New test covers the malformed JSON path

Malformed tool input JSON was silently replaced with an empty dict,
with no logging. The tool then ran with empty arguments and the model
had no indication its JSON was invalid.

Added a logger.warning() call before the fallback so operators can
detect and diagnose this in production.

Fixes strands-agents#2051
mkmeral
mkmeral previously approved these changes Apr 28, 2026
@mkmeral mkmeral self-assigned this Apr 28, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

logger.warning(
"Failed to parse tool input JSON for '%s': %s",
current_tool_use.get("name", "unknown"),
current_tool_use["input"][:200] if isinstance(current_tool_use.get("input"), str) else "",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: The log message doesn't follow the project's structured logging format defined in AGENTS.md.

The format requires field=<value> pairs separated by commas, followed by a pipe | and a lowercase human-readable message. Compare with the existing warning on line 349 in this same file:

logger.warning(
    "original_stop_reason=<%s>, new_stop_reason=<%s> | "
    "overriding stop reason due to toolUse blocks in response",
    "end_turn",
    "tool_use",
)

Suggestion:

logger.warning(
    "tool_name=<%s>, raw_input=<%s> | failed to parse tool input json, defaulting to empty dict",
    current_tool_use.get("name", "unknown"),
    current_tool_use["input"][:200] if isinstance(current_tool_use.get("input"), str) else "",
)

"redactedContent": b"",
},
),
# Tool Use - Malformed input JSON
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: The new parametrized test case verifies the state transformation (malformed JSON → {}) but doesn't assert that logger.warning was actually called. Since the purpose of this PR is to add the warning log, the test should verify it.

Suggestion: Add a dedicated test that uses unittest.mock.patch to assert the warning is emitted with the expected arguments:

@unittest.mock.patch("strands.event_loop.streaming.logger")
def test_handle_content_block_stop_logs_warning_on_malformed_json(mock_logger):
    state = {
        "content": [],
        "current_tool_use": {"toolUseId": "123", "name": "test_tool", "input": "{invalid json}"},
        "text": "",
        "reasoningText": "",
        "citationsContent": [],
        "redactedContent": b"",
    }

    strands.event_loop.streaming.handle_content_block_stop(state)

    mock_logger.warning.assert_called_once()
    call_args = mock_logger.warning.call_args
    assert "test_tool" in str(call_args)
    assert "{invalid json}" in str(call_args)

@github-actions
Copy link
Copy Markdown

Assessment: Comment

Good improvement for observability — logging malformed JSON will help operators debug tool invocation failures, especially with smaller models.

Review Details
  • Logging format: The warning message should follow the project's structured logging convention (field=<value> | message) as defined in AGENTS.md and used elsewhere in this file (see line 349).
  • Test coverage: The parametrized test verifies the state transformation but doesn't assert that the warning is actually logged. A dedicated test with a mocked logger would strengthen coverage for the core behavior this PR adds.

Clean, small, well-scoped change — just the two items above to address.

- Switch warning to project's field=<value> | message convention per AGENTS.md
- Add dedicated test that asserts logger.warning is emitted with the
  expected tool_name and raw_input on malformed JSON

Address auto-review feedback on strands-agents#2054.
@Ratansairohith
Copy link
Copy Markdown
Contributor Author

@mkmeral thanks for the approval! Addressed the auto-review feedback in 009fba5:

  • Switched the warning to the project's structured logging format (tool_name=<%s>, raw_input=<%s> | ...) per AGENTS.md.
  • Added a dedicated test (test_handle_content_block_stop_logs_warning_on_malformed_json) that patches the logger and asserts the warning is emitted with the expected args.

The check-api failure looks like it's from this branch being a few weeks behind maingriffe check --against main is flagging removals/renames on main (e.g. claude-sonnet-4-6claude-sonnet-4-20250514, dropped 'checkpoint' stop reason). Happy to rebase onto current main to clear it if you'd like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Malformed tool input JSON silently replaced with empty dict, no logging

2 participants