Skip to content

Subagents left with only ToolSearch and claude -p loses Agent/Bash when tool search defers tools behind a gateway (2.1.304+) #104309

Open

Description

@YangChen-AIdev

Subagents left with only ToolSearch and claude -p loses Agent/Bash when tool search defers tools behind a gateway (2.1.304+)

Summary

Since 2.1.304 turned tool search on by default behind a custom ANTHROPIC_BASE_URL, in that configuration:

  1. Subagents start with ToolSearch as their only tool. In a controlled run, a tools: * subagent began with nothing else and could not invoke Read at all. In the incident that led here, custom review agents whose definitions explicitly declare tools: Read, Grep, Glob, Bash could not read files either — i.e. explicit tools: declarations do not appear to exempt tools from deferral (the controlled on-state evidence below covers a tools: * agent; the declared-tools evidence is the incident report).
  2. claude -p (headless) loses its core built-in tools. The main process of a -p session has no Agent or Bash tool (Read untested directly, but see below), and ToolSearch cannot surface them either: its deferred pool yielded only Cron tools, WebFetch/WebSearch, EnterWorktree and mcp__* tools — none of the core built-ins (Agent, Bash, Read) could be found. Spawning a subagent or running a command is impossible by any means we tried.
  3. The only escape is ENABLE_TOOL_SEARCH=false, which restores full direct tools everywhere (verified below).

Environment

  • Claude Code 2.1.306 (experiments) / 2.1.308 (re-verification); macOS arm64; npm global install
  • Third-party Anthropic-compatible gateway via ANTHROPIC_BASE_URL, 1M-context third-party model (the gateway does not reject tool search — requests succeed, which is why the deferred-tools behavior activates)
  • Upgraded 2.1.301 → 2.1.306 on 2026-10-21; subagents worked normally on 2.1.301 the day before

Controlled runs (single variable across rows 2 vs 3: ENABLE_TOOL_SEARCH)

Probe task given to agents: list your initially available tools (no tool calls), note whether ToolSearch exists, then call Read on a known file directly (no search first).

# Process Tool search Result
1 Interactive session → general-purpose subagent (tools: *) on (default) Initial tools: only ToolSearch; Read: "not in this conversation's tool definitions, nor in the deferred tool list, cannot be invoked" (agent self-report, no tool calls made)
2 claude -p main process on (default) No Agent/Bash tool. ToolSearch tried with four queries incl. a broad fallback, plus a separate `bash
3 claude -p main process off Agent tool available and invoked — the spawn itself worked (that first subagent then failed on an unrelated model-name 400; re-run after temporarily pointing the agent at a model the gateway serves produced row 4)
4 claude -p --settings <file with {"env":{"ENABLE_TOOL_SEARCH":"false"}}> → custom agent (definition declares tools: Read, Grep, Glob, Bash) off Initial tools: Read, Bash (reported); no ToolSearch; direct Read of the file succeeded, returning its first line
5 Re-verification on 2.1.308, ENABLE_TOOL_SEARCH=false in settings, interactive session, general-purpose subagent (same type as row 1) off Full direct toolset: all built-ins (Agent, Bash, Read, Write, Edit, Skill, WebFetch, …) plus all configured MCP tools; no ToolSearch; direct Read succeeded

Rows 2 vs 3 are the clean single-variable pair (same claude -p command; on — the built-in Agent tool cannot be found through any means; off — it works). Rows 1 vs 4 are not a same-agent pair: row 1 is a tools: * agent, and our two attempts to probe an explicitly-declared custom agent with tool search on both failed for unrelated reasons (one agent type not yet registered, one model-name 400), which is why no on-state row exists for a tools:-declared agent. The on-state evidence for declared-tools agents is the 10-21 incident below.

Real-world impact (the incident that led here)

Hours after upgrading to 2.1.306, review subagents in interactive sessions could not read local files. One review agent improvised by using a browser-automation MCP tool's in-process JavaScript to read files (no page ever opened) to complete its review; another aborted. An independent investigation in a parallel session found three affected subagents sharing the symptom — including agents whose definitions declare tools: Read, Grep, Glob, Bash — and that agents searching ToolSearch with fully-anchored regexes (^(Bash|Read)$) got no matches, since ToolSearch matches description text, not tool names, leading agents to honestly report that tools do not exist.

Expected behavior

  1. Tools listed explicitly in a custom agent's tools: frontmatter should be connected directly, exempt from deferral (or the deferral should be documented and offer an opt-out per agent).
  2. A headless -p main process should not lose core built-in tools (Agent, Bash, Read). If they are deferred, ToolSearch must be able to find them; in our test it could not.
  3. Built-in tool discoverability through ToolSearch should not be this sensitive to search phrasing.

Workaround

"ENABLE_TOOL_SEARCH": "false" in settings env restores pre-2.1.304 behavior everywhere. (The tool-search token savings are lost, but it is currently the only way we found to keep subagents functional behind a gateway.)

Notes for triage

Activity

  1. axisrow commented on Oct 24, 2026

    @axisrow

    +1 — still reproduces on 2.1.310 (macOS arm64), and the affected surface is broader than subagents and claude -p: a main-process interactive TUI session behind a custom ANTHROPIC_BASE_URL (third-party 1M model) also started with only the deferred layer.

    Details captured from a session where this happened:

    • The model's tool definitions for the entire session lifetime contained only the deferred-layer tools (ToolSearch, WebFetch, SendMessage); Bash/Read/Write/Edit were never registered. No --allowedTools/--disallowedTools were passed and no deny rules exist in any settings file — this is not a permissions misconfiguration.
    • The tools that did load worked normally (e.g. WebFetch returned real HTTP responses), and ToolSearch itself worked too — but repeated queries over the first several minutes never surfaced Bash/Read/Write/Edit from the deferred pool, matching the "deferred pool lacks core built-ins" observation above. The model visibly spent its first minutes repeatedly querying ToolSearch for the missing core tools before giving up.
    • Relaunching the same session with --resume <id> (fresh CLI process) reproduced the broken toolset, while fresh sessions with new session ids on the same machine, same version, same gateway were healthy the same day — so it does not require a headless/subagent context, and resume does not self-heal it.
    • ENABLE_TOOL_SEARCH=false from this issue is the known escape hatch; not yet exercised on this particular repro.
  2. YangChen-AIdev commented on Oct 25, 2026

    @YangChen-AIdev
    Author

    Data point from my machine (macOS arm64, v2.1.308, third-party gateway via ANTHROPIC_BASE_URL, 1M-context model): we re-ran the probe with n=3 per arm to check the probabilistic-vs-deterministic question.

    • claude -p --settings '{"env":{"ENABLE_TOOL_SEARCH":"true"}}' × 3: 3/3 broken — the toolset consisted only of ToolSearch (the third run listed it under the internal name tool_search_tool_regex); Bash/Read were never present.
    • Tool search off × 6 (3 explicit --settings overrides, 3 inherited from the user-level settings file): 6/6 healthy — full direct toolset, no ToolSearch.

    So on this machine/version the flag looks deterministic rather than probabilistic. One pitfall worth flagging for anyone reproducing: once ENABLE_TOOL_SEARCH=false is set in the user-level settings file, a plain claude -p inherits it — probes meant to test the default silently become OFF probes. Explicit --settings overrides are needed to actually test the on state (this bit us once today).

    Re @axisrow's same-day mix of healthy and affected sessions: possibly the healthy ones never activated the deferred layer at all. Across all our runs we saw no middle state — it was either the full direct toolset with no ToolSearch, or ToolSearch-only. The per-session stickiness matches what we see — in every run the toolset stayed fixed for the whole session — and is consistent with your --resume observation. (Our 6/6 OFF runs also cover the escape-hatch check your repro hadn't exercised yet.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions