Skip to content

[Bug]: session-recovery hook doesn't handle 'unavailable tool' (dummy_tool) errors — agent stuck in unrecoverable loop #1803

@zrt-ai-lab

Description

@zrt-ai-lab

Prerequisites

  • I will write this issue in English (see our Language Policy)
  • I have searched existing issues to avoid duplicates
  • I am using the latest version of oh-my-opencode
  • I have read the documentation or asked an AI coding agent with this project's GitHub URL loaded and couldn't find the answer

Bug Description

When Sisyphus or its sub-agents (e.g., sisyphus-junior) are interrupted mid-execution (e.g., during session_list / session_read calls), the model sometimes hallucinates a tool call to a non-existent tool upon recovery. OpenCode then throws a dummy_tool error:

▣ Sisyphus · copilotcode-14 · interrupted
⚙ dummy_tool Model tried to call unavailable tool 'invalid'. Available tools: .

This error crashes the agent turn and is not recoverable by the existing session-recovery hook, because detectErrorType() only handles:

  • tool_result_missing
  • thinking_block_order
  • thinking_disabled_violation
  • assistant_prefill_unsupported

It does not handle the unavailable tool / NoSuchToolError case.

Root Cause Analysis

  1. Model hallucination: When an agent is interrupted (context overflow, timeout, etc.) and the session attempts to resume, the model sometimes generates a tool call to a tool named 'invalid' or other non-existent tool names.

  2. OpenCode's error handling: OpenCode throws NoSuchToolError (displayed as dummy_tool) instead of gracefully skipping the invalid tool call and continuing.

  3. Missing recovery path: The session-recovery hook (src/hooks/session-recovery/detect-error-type.ts) doesn't detect this error type, so no recovery is attempted.

Proposed Fix

Add "unavailable_tool" as a new RecoveryErrorType in the session-recovery hook:

In detect-error-type.ts:

export type RecoveryErrorType = 
  | "tool_result_missing" 
  | "thinking_block_order" 
  | "thinking_disabled_violation" 
  | "assistant_prefill_unsupported"
  | "unavailable_tool"  // NEW
  | null;

export function detectErrorType(error: unknown): RecoveryErrorType {
  const message = extractErrorMessage(error);
  if (!message) return null;
  
  // NEW: detect unavailable tool errors
  if (/tried to call unavailable tool/i.test(message) || 
      /dummy_tool/i.test(message)) {
    return "unavailable_tool";
  }
  
  // ... existing checks
}

Recovery strategy: When unavailable_tool is detected, strip the invalid tool call from the message history and retry the turn (similar to how tool_result_missing recovery works).

Steps to Reproduce

  1. Use a custom OpenAI-compatible provider (e.g., API proxy) that doesn't return usage data
  2. Run a complex task with ultrawork mode — Sisyphus delegates to sub-agents
  3. When context grows large, sub-agents get interrupted
  4. On resumption, model hallucinates invalid tool calls → dummy_tool error
  5. Session is stuck — no recovery possible

Expected Behavior

The session-recovery hook should detect the unavailable tool error and recover by stripping the invalid tool call, allowing the agent to continue working.

Actual Behavior

The error is unrecoverable. The agent shows dummy_tool errors repeatedly until the user manually starts a new session.

Environment

  • opencode: 1.1.60
  • oh-my-opencode: 3.5.2
  • Provider: Custom OpenAI-compatible (via API proxy, no usage in responses)
  • OS: macOS (arm64)
  • Model: claude-4.6-opus via proxy

Additional Context

This issue is exacerbated when:

  • The provider doesn't return token usage data (context window monitoring is blind)
  • Multiple background agents run concurrently
  • preemptive_compaction is enabled but can't fully prevent overflow

Related OpenCode issues:

Error Logs

⚙ session_list [limit=10]
⚙ session_read [session_id=ses_xxx, include_todos=true, limit=30]
Tool execution aborted
▣ Sisyphus · copilotcode-14 · interrupted
⚙ dummy_tool Model tried to call unavailable tool 'invalid'. Available tools: .
▣ Sisyphus · copilotcode-14 · interrupted
⚙ dummy_tool Model tried to call unavailable tool 'invalid'. Available tools: .
⚙ dummy_tool Model tried to call unavailable tool 'invalid'. Available tools: .

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions