Repository navigation
feat: Add Error Interception Middleware for guided AI self-correction #999
Description
Problem
When the AI model makes structural mistakes (e.g., passing cwd as an object instead of a string, nesting tool calls inside parameters, or emitting XML tool markup alongside native tool calls), Zoo Code currently:
- Shows raw error messages that the AI cannot parse or learn from
- Repeats the same mistake until
consecutiveMistakeLimitis reached - Displays an interactive "Continue" button that freezes the workflow until the user manually intervenes
This affects all models but is especially prevalent with MiMo models, where ~80% of diagnostics show CWD_OBJECT_MISUSE as the dominant failure mode.
Related Issues
This middleware addresses or provides infrastructure for the following existing bug reports:
Directly Covered by This Middleware
| Issue | Title | Pattern Coverage | How Middleware Helps |
|---|---|---|---|
| #752 | Shell Integration Warning when using MiMo | SHELL_INTEGRATION/001 |
When shell integration fails, middleware returns a guided message telling the AI to use non-shell tools instead of looping. Prevents the 10+ consecutive failure spiral described in the issue. |
| #704 | Shell Integration Error persists after long sessions | SHELL_INTEGRATION/001 |
Same as #752. The dead terminal reuse infinite loop is mitigated by the MODEL_STUCK_LOOP circuit breaker — after 3 failures, the AI is directed to change strategy entirely. |
| #800 | Terminal cannot capture results, dialog stuck | MODEL_STUCK_LOOP |
The circuit breaker prevents the "stuck at Running" state by resetting consecutiveMistakeCount and directing the AI to try a different approach after 3 occurrences. |
| #794 | Errors Produced by Open Weight Models + Solutions | All patterns | This issue's community patches (Guide G1/G2/G3, XML fallback P1) are the direct inspiration for this middleware. Our middleware formalizes the "guide the AI" philosophy into official, testable infrastructure with 9+1 categorized patterns instead of minified extension.js patches. |
| #547 | DeepSeek 5 structural tool-calling defects | PARAM_MISSING, XML_NATIVE_DUAL_PROTOCOL, CONTEXT_OVERFLOW |
Defect 1 (Missing Required Parameters) → PARAM_MISSING/001 pattern. Defect 4 (XML Tag Fallback) → XML_NATIVE_DUAL_PROTOCOL pattern strips XML and guides to native tool_use. Defect 3 (reasoning_content loss) → CONTEXT_OVERFLOW/001 auto-recovery. |
| #709 | Tool call serialization failure - XML output | XML_NATIVE_DUAL_PROTOCOL |
When mimo-v2.5-pro outputs XML tags alongside native calls, middleware strips the XML from visible text and merges a protocol guide into the native tool's result. The AI sees "Use native tool_use only" and self-corrects. |
| #707 | Chat output truncation at 8KB - XML tool calls as raw text | XML_NATIVE_DUAL_PROTOCOL |
Same pattern as #709. Middleware detects XML markers in text blocks and strips them before rendering. Combined with the 10,000-char text-length guard to prevent over-stripping on very long responses. |
| #695 | Streaming tool-call argument deltas silently dropped | PARAM_MISSING/001 |
When streaming data loss causes missing parameters, the PARAM_MISSING pattern returns a guided message with the tool's required parameters and correct usage example, instead of a cryptic "missing nativeArgs" error. |
| #459 | Claude model proxy errors/spinning | MODEL_STUCK_LOOP |
When the model enters an error loop, the circuit breaker at occurrence 3 resets consecutiveMistakeCount and issues a [MODEL_STUCK_LOOP] directive. The interactive "Continue" button is suppressed — the AI self-corrects. |
| #458 | Requests keep spinning | MODEL_STUCK_LOOP |
Same circuit breaker mechanism. Prevents indefinite spinning by directing the AI to change strategy. |
Indirectly Related (Middleware Provides Partial Coverage)
| Issue | Title | Relationship |
|---|---|---|
| #648 | Un-controllable Token Exceed with MCP results | CONTEXT_OVERFLOW/001 pattern provides auto-recovery guidance when context window is exceeded. Does not prevent the overflow itself, but guides the AI to compress or continue from summary. |
| #427 | Shell-integration polling refactor | SHELL_INTEGRATION/001 handles the error gracefully when shell integration fails, regardless of the underlying polling mechanism. Complementary fixes. |
| #471 | Infinite JSON tool loop on Ollama | MODEL_STUCK_LOOP circuit breaker prevents infinite loops. Does not fix the Ollama tool-calling detection issue itself. |
Not Covered (Outside Middleware Scope)
| Issue | Reason |
|---|---|
| #257 | Chat stuck on "API Request" — UI state management issue, not tool error |
| #877 | Ollama model refresh stuck — settings panel UI issue |
| #937 | Message processing order at terminal finish — task lifecycle issue |
| #856 | Task Tree Abstraction — architecture enhancement |
| #464 | Universal AI Clipboard — new feature proposal |
Solution: Error Interception Middleware
A new middleware layer that intercepts, classifies, and transforms tool errors into actionable guidance messages, enabling the AI to self-correct without user intervention.
Architecture
Tool Error → ErrorClassifier → ErrorPattern DB (9+1 categories)
↓
MessageTransformer → Versioned JSON guidance
↓
TaskErrorState (WeakMap) → Circuit Breaker
↓
Exactly one guided tool_result → AI self-corrects
New Modules
| Module | Purpose |
|---|---|
errorPatterns.ts |
Pattern DB with 9+1 error categories and variant entries |
ErrorClassifier.ts |
Deterministic error classifier (priority-ordered pattern matching) |
MessageTransformer.ts |
Versioned JSON guidance serializer (WHAT/WHY/NEXT format) |
TaskErrorState.ts |
Task-scoped persistent state via WeakMap (occurrence tracking, circuit breaker) |
StructuralValidator.ts |
CWD object detection + nested parameter overflow detection |
ToolErrorInterceptor.ts |
Interceptor facade (transformError, circuit status) |
Error Patterns Covered
| # | Category | Pattern ID | Trigger | Behavior | Related Issues |
|---|---|---|---|---|---|
| 1 | DUPLICATE_CALL | EI/DUPLICATE_CALL/001 | Repeated identical tool call | Block + guide | — |
| 2 | PARAM_MISSING | EI/PARAM_MISSING/001 | Required parameter missing | Block + guide with example | #695, #547 Defect 1 |
| 3 | PARAM_TYPE_MISMATCH | EI/PARAM_TYPE_MISMATCH/001 | Generic type mismatch | Block + guide | — |
| 4 | PARAM_TYPE_MISMATCH | EI/PARAM_TYPE_MISMATCH/002 | CWD passed as object | Block + guide "cwd must be a string" | — |
| 5 | PARAM_TYPE_MISMATCH | EI/PARAM_TYPE_MISMATCH/003 | Nested tool invocation in param | Block + guide "issue separate calls" | — |
| 6 | FILE_NOT_FOUND | EI/FILE_NOT_FOUND/001 | File/directory not found | Guide to use list_files | — |
| 7 | SHELL_INTEGRATION | EI/SHELL_INTEGRATION/001 | Shell integration failure | Guide to use non-shell tools | #752, #704, #427 |
| 8 | MCP_TOOL_MISSING | EI/MCP_TOOL_MISSING/001 | MCP tool/server not found | Guide to select from list | — |
| 9 | INVALID_TOOL_PROTOCOL | EI/INVALID_TOOL_PROTOCOL/001 | XML tool calls in text | Strip XML + guide | #709, #707 |
| 10 | INVALID_TOOL_PROTOCOL | EI/INVALID_TOOL_PROTOCOL/002 | XML + native dual protocol | Strip XML + merge protocol guide | #709, #707, #547 Defect 4 |
| 11 | CONTEXT_OVERFLOW | EI/CONTEXT_OVERFLOW/001 | Context window exceeded | Auto-recovery guide | #648, #547 Defect 3 |
| 12 | MODEL_STUCK_LOOP | (circuit breaker) | Same error 3x | Strategy change directive, no user prompt | #800, #459, #458, #752, #704 |
Circuit Breaker (MODEL_STUCK_LOOP)
| Occurrence | Behavior |
|---|---|
| 1 | Guided correction (variant-specific message) |
| 2 | Strengthened guidance ("Re-read the tool schema now") |
| 3 | Circuit opens — [MODEL_STUCK_LOOP] directive, consecutiveMistakeCount reset, no user prompt |
Design Philosophy (from #794)
"We have two options: (1) Suppress error messages and let the AI self-correct, (2) When the AI exhibits a bad habit that causes errors, guide it toward the correct approach. We prefer option 2."
This middleware implements option 2 as official, testable infrastructure. Each error produces a WHAT/WHY/NEXT guidance payload that tells the AI:
- WHAT went wrong (specific, not generic)
- WHY it happened (root cause, not symptom)
- NEXT what to do (actionable steps, not vague advice)
Before vs After
| Scenario | Before | After |
|---|---|---|
AI sends cwd: {command: "..."} |
Terminal error → raw message → AI repeats → user must click Continue | Preflight blocks → guided tool_result with "cwd must be a string" → AI self-corrects |
| AI emits XML + native tool call | Confusing partial execution → user must intervene (#709, #707) | XML stripped, native executes, protocol guide merged |
| AI nests parallel params in one call | Cryptic error → user must Continue | Preflight detects nested signature → guided "issue separate calls" |
| Same mistake 3x | consecutiveMistakeLimit → interactive prompt → user must click (#800, #459) |
Circuit breaker → [MODEL_STUCK_LOOP] → AI changes strategy |
| Shell integration failure | Raw error → AI retries same terminal → infinite loop (#752, #704) | SHELL_INTEGRATION/001 guide → AI uses non-shell tools |
Test Coverage
- 113 tests across 7 test files, all passing
- StructuralValidator: CWD detection, nested param detection, allow-list, cycle detection
- TaskErrorState: Occurrence tracking, circuit open/close, fingerprint, reset, WeakMap isolation
- presentAssistantMessage: 7 integration tests (CWD blocked, circuit at occurrence 3, nested overflow, XML+native, etc.)
- model-stuck-loop: 10 tests (accumulation, threshold, suppression, per-Task isolation)
Files Changed
| File | Type | Description |
|---|---|---|
src/core/tools/error-interception/errorPatterns.ts |
Modified | 3 variant entries (CWD, NESTED, XML_DUAL) |
src/core/tools/error-interception/TaskErrorState.ts |
New | Task-scoped persistent state (WeakMap) |
src/core/tools/error-interception/StructuralValidator.ts |
New | CWD + nested param detection |
src/core/assistant-message/presentAssistantMessage.ts |
Modified | Structural preflight + XML detection + FP-1 fix (classify errors by message content) |
src/core/task/Task.ts |
Modified | MODEL_STUCK_LOOP circuit breaker |
src/core/tools/error-interception/index.ts |
Modified | Stable exports |
| Test files (4 new) | New | 113 tests total |
Commits
fc38100a6— feat: complete gap-fix for 4 uncovered error patterns (+1460/-2)291f4594b— fix: resolve false positive PARAM_TYPE_MISMATCH on validateToolUse failures (+13/-1)
Activity