BUG-HUNT: [error-handling] ToolRunner.execute() uses allow_nan=True for host-routed tools allowing NaN/Infinity in outputs that break JSON serialization downstream #7371

Open
opened 2026-04-10 18:22:21 +00:00 by HAL9000 · 3 comments
Owner

Bug Report: [error-handling] ToolRunner.execute() uses json.dumps with allow_nan=True for host-routed tools allowing invalid JSON values that break downstream consumers

Severity Assessment

  • Impact: Tool outputs containing float('nan'), float('inf'), or float('-inf') values pass the host-routed validation check but produce invalid JSON when actually serialized by downstream consumers (e.g., LLM API calls), causing cryptic JSONDecodeError or ValueError failures far from the source
  • Likelihood: Medium — any numeric computation tool that could produce NaN/Infinity values (statistical operations, division, log of negative numbers, etc.)
  • Priority: Medium

Location

  • File: src/cleveragents/tool/runner.py
  • Function/Class: ToolRunner.execute()
  • Lines: ~220-250

Description

The ToolRunner.execute() method validates inputs and outputs using json.dumps(). However, it uses allow_nan=True (Python's default) for host-routed tools, while using allow_nan=False (RFC 7159 compliant) for container-routed tools.

The comment in the code acknowledges this:

# Validate that output is JSON-serialisable.  Host-routed tools
# use the default ``allow_nan=True`` for backward compatibility.
try:
    json.dumps(raw_output)  # allow_nan=True by default!

The problem is that json.dumps({"value": float('nan')}) succeeds (produces {"value": NaN}) but NaN is not valid JSON. When this output is later sent to an LLM API or saved to a file, it will fail. The validation is misleading — it passes when the actual JSON standard would reject it.

Evidence

# From tool/runner.py

# Container path - correctly uses allow_nan=False:
try:
    json.dumps(inputs, allow_nan=False)  # RFC 7159 compliant
except (TypeError, ValueError) as exc:
    return ToolResult(...)

# Host path - incorrectly uses default allow_nan=True:
try:
    json.dumps(inputs)  # allow_nan=True by default - accepts NaN/Infinity!
except (TypeError, ValueError) as exc:
    ...

# Output validation also uses allow_nan=True:
try:
    json.dumps(raw_output)  # allow_nan=True - accepts NaN/Infinity!
except (TypeError, ValueError) as exc:
    ...

Python's default json.dumps(float('nan')) produces 'NaN' which is not valid JSON. The JSON specification (RFC 7159/8259) explicitly states that NaN and Infinity are not allowed values. When this gets sent to an LLM API's messages parameter, the API will either reject it or behave unexpectedly.

Expected Behavior

Both host-routed and container-routed tools should validate outputs with allow_nan=False to ensure they produce RFC 7159-compliant JSON. The backward-compatibility comment suggests this was intentionally weakened, but the risk outweighs the benefit.

Actual Behavior

Host-routed tool outputs containing NaN, Infinity, or -Infinity float values pass validation but produce invalid JSON that breaks downstream consumers. This creates a confusing debugging situation where the tool "succeeds" but the LLM API call fails with a cryptic error.

Suggested Fix

# Both paths should use allow_nan=False:
try:
    json.dumps(raw_output, allow_nan=False)  # RFC 7159 compliant
except (TypeError, ValueError) as exc:
    return ToolResult(
        success=False,
        output={},
        error=f"Output contains non-JSON values (NaN/Infinity): {exc}",
        duration_ms=elapsed,
    )

Category

error-handling

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_, and @tdd_expected_fail to prove the bug exists before fixing it.


Automated by CleverAgents Bot
Supervisor: Bug Detection Pool | Agent: bug-hunt-pool-supervisor

## Bug Report: [error-handling] ToolRunner.execute() uses json.dumps with allow_nan=True for host-routed tools allowing invalid JSON values that break downstream consumers ### Severity Assessment - **Impact**: Tool outputs containing `float('nan')`, `float('inf')`, or `float('-inf')` values pass the host-routed validation check but produce invalid JSON when actually serialized by downstream consumers (e.g., LLM API calls), causing cryptic `JSONDecodeError` or `ValueError` failures far from the source - **Likelihood**: Medium — any numeric computation tool that could produce NaN/Infinity values (statistical operations, division, log of negative numbers, etc.) - **Priority**: Medium ### Location - **File**: `src/cleveragents/tool/runner.py` - **Function/Class**: `ToolRunner.execute()` - **Lines**: ~220-250 ### Description The `ToolRunner.execute()` method validates inputs and outputs using `json.dumps()`. However, it uses `allow_nan=True` (Python's default) for **host-routed** tools, while using `allow_nan=False` (RFC 7159 compliant) for **container-routed** tools. The comment in the code acknowledges this: ```python # Validate that output is JSON-serialisable. Host-routed tools # use the default ``allow_nan=True`` for backward compatibility. try: json.dumps(raw_output) # allow_nan=True by default! ``` The problem is that `json.dumps({"value": float('nan')})` succeeds (produces `{"value": NaN}`) but `NaN` is not valid JSON. When this output is later sent to an LLM API or saved to a file, it will fail. The validation is misleading — it passes when the actual JSON standard would reject it. ### Evidence ```python # From tool/runner.py # Container path - correctly uses allow_nan=False: try: json.dumps(inputs, allow_nan=False) # RFC 7159 compliant except (TypeError, ValueError) as exc: return ToolResult(...) # Host path - incorrectly uses default allow_nan=True: try: json.dumps(inputs) # allow_nan=True by default - accepts NaN/Infinity! except (TypeError, ValueError) as exc: ... # Output validation also uses allow_nan=True: try: json.dumps(raw_output) # allow_nan=True - accepts NaN/Infinity! except (TypeError, ValueError) as exc: ... ``` Python's default `json.dumps(float('nan'))` produces `'NaN'` which is **not valid JSON**. The JSON specification (RFC 7159/8259) explicitly states that `NaN` and `Infinity` are not allowed values. When this gets sent to an LLM API's `messages` parameter, the API will either reject it or behave unexpectedly. ### Expected Behavior Both host-routed and container-routed tools should validate outputs with `allow_nan=False` to ensure they produce RFC 7159-compliant JSON. The backward-compatibility comment suggests this was intentionally weakened, but the risk outweighs the benefit. ### Actual Behavior Host-routed tool outputs containing `NaN`, `Infinity`, or `-Infinity` float values pass validation but produce invalid JSON that breaks downstream consumers. This creates a confusing debugging situation where the tool "succeeds" but the LLM API call fails with a cryptic error. ### Suggested Fix ```python # Both paths should use allow_nan=False: try: json.dumps(raw_output, allow_nan=False) # RFC 7159 compliant except (TypeError, ValueError) as exc: return ToolResult( success=False, output={}, error=f"Output contains non-JSON values (NaN/Infinity): {exc}", duration_ms=elapsed, ) ``` ### Category error-handling ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_<this-issue-number>, and @tdd_expected_fail to prove the bug exists before fixing it. --- **Automated by CleverAgents Bot** Supervisor: Bug Detection Pool | Agent: bug-hunt-pool-supervisor
Author
Owner

Verified — Bug: ToolRunner allows NaN/Infinity in outputs breaking downstream JSON serialization. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Bug: ToolRunner allows NaN/Infinity in outputs breaking downstream JSON serialization. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Bug: ToolRunner allows NaN/Infinity in outputs breaking downstream JSON serialization. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Bug: ToolRunner allows NaN/Infinity in outputs breaking downstream JSON serialization. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Bug: ToolRunner allows NaN/Infinity in outputs breaking downstream JSON serialization. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Bug: ToolRunner allows NaN/Infinity in outputs breaking downstream JSON serialization. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#7371
No description provided.