hermes - 💡(How to fix) Fix [BUG] Context compaction truncates tool_call.function.arguments mid-string, produces invalid JSON, causes 400 errors with strict providers (e.g. MiniMax) [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#13574Fetched 2026-04-22 08:05:43
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
0
Timeline (top)
labeled ×4commented ×2

agent/context_compressor.py truncates tool_calls[*].function.arguments by raw string slicing (args[:200] + "...[truncated]"), which breaks JSON validity. Providers that strictly validate arguments as JSON (confirmed: MiniMax api.minimaxi.com) return HTTP 400 on subsequent requests, persistently poisoning the session because the truncated tool_call stays in history and gets replayed every turn.

Error Message

All three report the same MiniMax (2013) error pattern. None diagnosed

Root Cause

agent/context_compressor.py around line 461 (Pass 3 of _prune_old_tool_results):

if len(args) > 500:
    tc = {**tc, "function": {**tc["function"],
                             "arguments": args[:200] + "...[truncated]"}}

Raw string chop doesn't preserve JSON validity. If truncation falls inside a string value, the value's closing quote (plus any nested structure) is lost. The concatenated "...[truncated]" suffix is literal text, not valid JSON continuation.

Code Example

HTTP 400 invalid function arguments json string,
   tool_call_id: call_function_XXX (2013)

---

if len(args) > 500:
    tc = {**tc, "function": {**tc["function"],
                             "arguments": args[:200] + "...[truncated]"}}

---

if len(args) > 500:
    try:
        parsed = json.loads(args)
        keys = list(parsed.keys()) if isinstance(parsed, dict) else []
        stub = json.dumps({
            "_truncated": True,
            "_original_length": len(args),
            "_keys": keys,
        }, ensure_ascii=False)
    except (json.JSONDecodeError, TypeError):
        stub = json.dumps({
            "_truncated": True,
            "_original_length": len(args),
            "_preview": args[:150],
        }, ensure_ascii=False)
    tc = {**tc, "function": {**tc["function"], "arguments": stub}}
    modified = True
RAW_BUFFERClick to expand / collapse

Summary

agent/context_compressor.py truncates tool_calls[*].function.arguments by raw string slicing (args[:200] + "...[truncated]"), which breaks JSON validity. Providers that strictly validate arguments as JSON (confirmed: MiniMax api.minimaxi.com) return HTTP 400 on subsequent requests, persistently poisoning the session because the truncated tool_call stays in history and gets replayed every turn.

Reproduction

  1. Main model: MiniMax-M2.7 (any strict-JSON provider would trigger this)
  2. Trigger a tool_call with long arguments (e.g. skill_manage with multi-KB markdown content field)
  3. Let conversation grow until context compaction kicks in (compression.threshold default 0.5)
  4. Post-compaction, observe tool_calls[0].function.arguments truncated to {"action": "create", "content": "# ...[truncated] — JSON string value with unterminated quote
  5. Next API call returns:
    HTTP 400 invalid function arguments json string,
    tool_call_id: call_function_XXX (2013)
  6. Every subsequent turn re-sends this broken history → persistent 400 → session must be archived to recover.

Root cause

agent/context_compressor.py around line 461 (Pass 3 of _prune_old_tool_results):

if len(args) > 500:
    tc = {**tc, "function": {**tc["function"],
                             "arguments": args[:200] + "...[truncated]"}}

Raw string chop doesn't preserve JSON validity. If truncation falls inside a string value, the value's closing quote (plus any nested structure) is lost. The concatenated "...[truncated]" suffix is literal text, not valid JSON continuation.

Proposed fix

Replace the string slice with a JSON-valid stub that preserves the fingerprint of the original call:

if len(args) > 500:
    try:
        parsed = json.loads(args)
        keys = list(parsed.keys()) if isinstance(parsed, dict) else []
        stub = json.dumps({
            "_truncated": True,
            "_original_length": len(args),
            "_keys": keys,
        }, ensure_ascii=False)
    except (json.JSONDecodeError, TypeError):
        stub = json.dumps({
            "_truncated": True,
            "_original_length": len(args),
            "_preview": args[:150],
        }, ensure_ascii=False)
    tc = {**tc, "function": {**tc["function"], "arguments": stub}}
    modified = True

Related community reports (all closed as not planned)

All three report the same MiniMax (2013) error pattern. None diagnosed that the bug is in the client-side compaction module, not in the API layer.

Impact

  • MiniMax (confirmed) — any session that triggers compaction with large tool_call arguments becomes permanently broken
  • Likely affects other strict-JSON providers
  • Permissive providers (OpenAI, Anthropic) may accept the truncated arguments silently, leading to subtle mis-behaviors in replayed history

Environment

  • Hermes Agent v0.10.0 (v2026.4.16)
  • Python 3.11.15
  • MiniMax-M2.7 via api.minimaxi.com/v1 (OpenAI-compatible chat_completions)
  • Ubuntu (Tencent Cloud Shanghai)

extent analysis

TL;DR

Replace the raw string slicing in agent/context_compressor.py with a JSON-valid stub to preserve the fingerprint of the original call and prevent JSON validity issues.

Guidance

  • Identify the line of code causing the issue (around line 461 in agent/context_compressor.py) and replace the string slice with a JSON-valid stub as proposed in the issue.
  • Verify that the new stub preserves the fingerprint of the original call and does not truncate the string in a way that breaks JSON validity.
  • Test the fix with a strict-JSON provider like MiniMax to ensure that the session is no longer poisoned by the truncated tool_call.
  • Consider testing with other providers to ensure the fix does not introduce any new issues.

Example

The proposed fix provides a JSON-valid stub that preserves the fingerprint of the original call:

if len(args) > 500:
    try:
        parsed = json.loads(args)
        keys = list(parsed.keys()) if isinstance(parsed, dict) else []
        stub = json.dumps({
            "_truncated": True,
            "_original_length": len(args),
            "_keys": keys,
        }, ensure_ascii=False)
    except (json.JSONDecodeError, TypeError):
        stub = json.dumps({
            "_truncated": True,
            "_original_length": len(args),
            "_preview": args[:150],
        }, ensure_ascii=False)
    tc = {**tc, "function": {**tc["function"], "arguments": stub}}
    modified = True

Notes

The fix assumes that the json library is available and that the args string can be parsed as JSON. If this is not the case, additional error handling may be necessary.

Recommendation

Apply the proposed workaround by replacing the raw string slicing with a JSON-valid stub, as this should prevent the JSON validity issues and persistent 400 errors.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [BUG] Context compaction truncates tool_call.function.arguments mid-string, produces invalid JSON, causes 400 errors with strict providers (e.g. MiniMax) [2 comments, 2 participants]