Skip to content

fix: harden JSON misformat handling to prevent infinite loops and crashes#1245

Open
PaoloC68 wants to merge 1 commit intoagent0ai:developmentfrom
PaoloC68:fix/json-misformat-resilience
Open

fix: harden JSON misformat handling to prevent infinite loops and crashes#1245
PaoloC68 wants to merge 1 commit intoagent0ai:developmentfrom
PaoloC68:fix/json-misformat-resilience

Conversation

@PaoloC68
Copy link
Contributor

@PaoloC68 PaoloC68 commented Mar 12, 2026

Summary

Fixes the infinite misformat loop that affects 5+ reported issues. The agent's process_tools had a gap where parseable-but-invalid JSON (e.g., dict without tool_name) would raise RepairableException, get swallowed by _50_handle_repairable_exception, and loop forever without hitting the circuit breaker.

Changes

agent.py — Circuit breaker covers both failure modes

  • Case A (already worked): json_parse_dirty returns None → misformat counter increments
  • Case B (NEW fix): validate_tool_request raises RepairableException → counter now increments via try/except wrapper in process_tools
  • Moved counter reset to right after validation passes (format correctness, not tool existence)
  • At 5 consecutive failures, raises HandledException to stop the monologue gracefully

helpers/extract_tools.py — Brace-depth JSON extraction (unchanged from v1)

  • Replaces buggy rfind('}') with character-by-character brace-depth tracking
  • Handles nested braces, string-escaped braces, escaped quotes

prompts/fw.msg_misformat.md — Improved misformat prompt

  • Shows attempt countdown (attempt X/5) so model knows it's running out of retries
  • Removed contradictory code-fence example (told model "no fences" but showed one)
  • Compressed to single-line JSON example

tests/test_misformat_circuit_breaker.py — 73 tests

Suite Tests Coverage
TestExtractJsonObjectString 22 Brace-depth tracking, rfind regression, escapes, edge cases
TestJsonParseDirty 12 Parse, dirty JSON, non-string input, arrays
TestValidateToolRequest 14 None guard, RepairableException (not ValueError), all invalid shapes
TestCircuitBreakerCaseA 4 None parse → counter accumulates → breaker at 5
TestCircuitBreakerCaseB 6 Invalid dict → counter accumulates → breaker at 5
TestCircuitBreakerMixed 5 Mixed A+B accumulate, reset, boundary conditions
TestEndToEndPipeline 10 Realistic messages, rfind bug scenario, session reproduction

Root cause analysis

The _50_handle_repairable_exception extension (line 23) sets data["exception"] = None, swallowing the RepairableException and continuing the monologue loop. Meanwhile, the _error_retry plugin explicitly skips RepairableException (line 21). So for Case B, no circuit breaker ever fired — the counter was only in the else branch (Case A).

Workaround for models with frequent misformats

Thinking/reasoning models (e.g., MiniMax M2.5) produce reasoning traces with incidental { characters that confuse the JSON parser. To mitigate, add this to Chat model additional parameters in Agent Zero settings:

response_format={"type": "json_object"}

This forces JSON output at the token generation level. Confirmed supported on OpenRouter for: MiniMax M2.5, DeepSeek V3, Gemini 2.5 Flash, Grok 4.1 Fast, GPT-OSS-120B.

Testing

pytest tests/test_misformat_circuit_breaker.py -v
# 73 passed in 0.26s

…shes

Circuit breaker now covers BOTH failure modes:
- Case A: complete parse failure (tool_request is None)
- Case B: parseable-but-invalid JSON (RepairableException from validation)

Previously, Case B bypassed the consecutive_misformat counter entirely
because RepairableException was swallowed by _50_handle_repairable_exception
extension, allowing the agent to loop forever on models producing JSON
without required tool_name/tool_args fields.

Changes:
- agent.py: Wrap validate_tool_request in try/except RepairableException
  inside process_tools; increment counter for Case B; move counter reset
  to after validation passes (not after tool execution)
- prompts/fw.msg_misformat.md: Add attempt countdown, remove contradictory
  code-fence example, compress to single-line JSON example
- tests/test_misformat_circuit_breaker.py: 73 tests covering extraction,
  validation, circuit breaker (both cases), and end-to-end pipeline

Workaround: Models producing frequent misformats (especially thinking/reasoning
models like MiniMax M2.5) can be improved by adding response_format to the
Chat model additional parameters in Agent Zero settings:
  response_format={"type": "json_object"}
This forces JSON output at the token generation level. Confirmed supported
on OpenRouter for MiniMax M2.5, DeepSeek V3, Gemini 2.5 Flash, Grok 4.1.
@PaoloC68 PaoloC68 force-pushed the fix/json-misformat-resilience branch from 94656ea to b2b70a7 Compare March 19, 2026 18:54
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.

1 participant