Skip to content

fix(agent): repair malformed tool_call arguments before API send#12252

Closed
sirEven wants to merge 1 commit into
NousResearch:mainfrom
sirEven:fix/tool-call-arg-repair
Closed

fix(agent): repair malformed tool_call arguments before API send#12252
sirEven wants to merge 1 commit into
NousResearch:mainfrom
sirEven:fix/tool-call-arg-repair

Conversation

@sirEven

@sirEven sirEven commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a JSON repair pipeline at the pre-send normalization point in run_agent.py (~L9179) to fix malformed tool_call.arguments before they reach the API. Currently, when json.loads() fails inside the except Exception: pass block, the invalid JSON is silently passed through to the API — which rejects it with HTTP 400 "invalid tool call arguments", crashing the entire session.

Related Issue

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • run_agent.py ~L9179: Replaced except Exception: pass with a repair pipeline:
    1. Empty/whitespace-only arguments → "{}"
    2. Python None literal → "{}"
    3. Strip trailing commas before } or ]
    4. Auto-close unclosed { and [
    5. Remove excess closing delimiters
    6. Last resort: replace with "{}" (saves session, loses args — logged at WARNING)
  • All repairs logged at WARNING level for observability

How to Test

  1. Use a model that produces malformed tool_call arguments (e.g., GLM-5.1 via Ollama proxy)
  2. Without this fix: agent crashes with HTTP 400, session becomes permanently stuck
  3. With this fix: agent logs the repair with WARNING, continues execution

Manual reproduction — in a session where the model generates truncated arguments:

  • Previously: except Exception: pass → 400 error → session death spiral
  • Now: repaired to valid JSON → session continues

Tested with live hermes chat session — zero 400 errors after patch. Confirmed Ollama proxy rejects broken args (400) and accepts repaired args (200).

How this differs from existing PRs

PR Scope Location Approach
#11617 Compressor truncation context_compressor.py Changes Pass 3 truncation format
#11788 Compressor truncation context_compressor.py JSON-safe truncation + tests
#6691 Kimi models only New kimi_json_sanitizer.py Model-specific regex sanitizer
#5071 Persistence filter Message persistence Filter malformed calls on save
This PR All models run_agent.py pre-send Model-agnostic repair at last line of defense

This PR is complementary, not competing — it catches what slips through upstream. It operates at the pre-send normalization point, which is the last chance to fix broken args before the API call goes out. Even if compressor fixes (#11617, #11788) land, models can still produce malformed args at inference time (truncation, Python None, etc.) — this catches those cases.

Checklist

Models like GLM-5.1 can produce invalid JSON in tool_call arguments
(truncated strings, trailing commas, Python None, empty strings). The
current code catches the exception from json.loads() but silently
passes, allowing the broken JSON through to the API — which rejects
it with HTTP 400 and crashes the entire session.

This patch adds a repair pipeline at the pre-send normalization point
(~L9179 in run_agent.py):

1. Empty/whitespace → '{}'
2. Python None literal → '{}'
3. Strip trailing commas before }/]
4. Auto-close unclosed { and [
5. Remove excess closing delimiters
6. Last resort: replace with '{}' (saves session, loses args)

All repairs are logged at WARNING level for observability.

The fix is model-agnostic — it only activates on the except branch
when json.loads() fails on tool_call arguments, so there is zero
overhead for valid JSON. It acts as a last line of defense before
the API request goes out, complementing upstream fixes that address
specific sources of corruption (compressor truncation, per-model
sanitizers, etc.).

Tested: live session with hermes chat, no 400 errors after patch.
Ollama proxy rejects broken args (400), accepts repaired args (200).
teknium1 pushed a commit that referenced this pull request Apr 20, 2026
Cherry-picked from PR #12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
teknium1 added a commit that referenced this pull request Apr 20, 2026
Follow-up for PR #12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
teknium1 pushed a commit that referenced this pull request Apr 20, 2026
Cherry-picked from PR #12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
teknium1 added a commit that referenced this pull request Apr 20, 2026
Follow-up for PR #12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #13005 (#13005). Your commit was cherry-picked onto current main with your authorship preserved in git log. Follow-up: extracted to a testable helper, bounded the loop, added 17 tests. Thanks @sirEven!

ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
Cherry-picked from PR NousResearch#12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
Follow-up for PR NousResearch#12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
Cherry-picked from PR NousResearch#12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
Follow-up for PR NousResearch#12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
Luminet2023 pushed a commit to Luminet2023/hermes-agent that referenced this pull request May 1, 2026
Cherry-picked from PR NousResearch#12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
Luminet2023 pushed a commit to Luminet2023/hermes-agent that referenced this pull request May 1, 2026
Follow-up for PR NousResearch#12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
Cherry-picked from PR NousResearch#12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
Follow-up for PR NousResearch#12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
Cherry-picked from PR NousResearch#12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
Follow-up for PR NousResearch#12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
Cherry-picked from PR NousResearch#12252 by @sirEven.

Models like GLM-5.1 via Ollama can produce malformed tool_call arguments
(truncated JSON, trailing commas, Python None). The existing except
Exception: pass silently passes broken args to the API, which rejects
them with HTTP 400, crashing the session.

Adds a multi-stage repair pipeline at the pre-send normalization point:
1. Empty/whitespace-only → {}
2. Python None literal → {}
3. Strip trailing commas
4. Auto-close unclosed brackets
5. Remove excess closing delimiters
6. Last resort: replace with {} (logged at WARNING)
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
Follow-up for PR NousResearch#12252 salvage:
- Extract 75-line inline repair block to _repair_tool_call_arguments()
  module-level helper for testability and readability
- Remove redundant 'import re as _re' (re already imported at line 33)
- Bound the while-True excess-delimiter removal loop to 50 iterations
- Add 17 tests covering all 6 repair stages
- Add sirEven to AUTHOR_MAP in release.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants