Skip to content

fix: validate tool calls before persisting to prevent session poisoning#4724

Closed
Bartok9 wants to merge 1 commit into
NousResearch:mainfrom
Bartok9:fix/validate-tool-calls-before-persist
Closed

fix: validate tool calls before persisting to prevent session poisoning#4724
Bartok9 wants to merge 1 commit into
NousResearch:mainfrom
Bartok9:fix/validate-tool-calls-before-persist

Conversation

@Bartok9

@Bartok9 Bartok9 commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Problem

Malformed tool calls can poison a session and cause repeated 400 errors when replayed to strict providers. Once invalid tool calls are persisted in session history, every subsequent request fails because the same malformed history is replayed.

Observed failure mode:

HTTP 400: InternalError.Algo.InvalidParameter: The "function.arguments" parameter of the code model must be in JSON format.

This happens when persisted tool calls contain:

  • Empty function.name
  • Empty function.arguments string
  • Non-JSON or invalid JSON arguments
  • JSON arguments that are arrays or primitives instead of objects

Solution

Adds two layers of defense:

1. _is_valid_tool_call() - Validation at persist time

Validates tool calls before persisting to session history in _build_assistant_message(). Invalid tool calls are logged and skipped rather than persisted:

if not self._is_valid_tool_call(tool_call):
    logging.warning(f"Skipping malformed tool call: name={fn_name!r}, arguments=...")
    continue

2. _sanitize_tool_calls_for_strict_api() - Enhanced API-level filtering

Provides a second layer of defense by filtering malformed tool calls when preparing messages for API calls. This helps with existing poisoned sessions.

Validation rules

  • Function name must be a non-empty string
  • Arguments must be valid JSON object (dict), not array or primitive
  • Empty arguments string is invalid (should be "{}" at minimum)
  • None arguments are allowed (some providers omit for empty args)

Testing

Added comprehensive test coverage in tests/test_tool_call_validation.py:

  • 18 tests covering all validation scenarios
  • Tests for both validation function and sanitization function
  • All tests pass ✅

Fixes

Fixes #4662

Malformed tool calls (empty function name, invalid JSON arguments) can
poison a session and cause repeated 400 errors when replayed to strict
providers. This fix adds two layers of defense:

1. _is_valid_tool_call(): Validates tool calls before persisting to
   session history in _build_assistant_message(). Invalid tool calls are
   logged and skipped rather than persisted.

2. _sanitize_tool_calls_for_strict_api(): Enhanced to also filter
   malformed tool calls when preparing messages for API calls. This
   provides a second layer of defense for existing poisoned sessions.

Validation rules:
- Function name must be a non-empty string
- Arguments must be valid JSON object (dict), not array or primitive
- Empty arguments string is invalid (should be "{}" at minimum)
- None arguments are allowed (some providers omit for empty args)

Includes comprehensive test coverage for both validation functions.

Fixes NousResearch#4662
@Bartok9

Bartok9 commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #14407 — rebased onto current main with full coverage.

@Bartok9 Bartok9 closed this Apr 23, 2026
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder labels Apr 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Superseded by #14407 (rebased version).

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

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Malformed persisted tool calls can poison a session and cause repeated 400 errors on subsequent requests

3 participants