Repository navigation
Description
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:
- Subagents start with
ToolSearchas their only tool. In a controlled run, atools: *subagent began with nothing else and could not invokeReadat all. In the incident that led here, custom review agents whose definitions explicitly declaretools: Read, Grep, Glob, Bashcould not read files either — i.e. explicittools:declarations do not appear to exempt tools from deferral (the controlled on-state evidence below covers atools: *agent; the declared-tools evidence is the incident report). claude -p(headless) loses its core built-in tools. The main process of a-psession has no Agent or Bash tool (Read untested directly, but see below), andToolSearchcannot surface them either: its deferred pool yielded only Cron tools, WebFetch/WebSearch, EnterWorktree andmcp__*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.- 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
- 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). - A headless
-pmain process should not lose core built-in tools (Agent, Bash, Read). If they are deferred,ToolSearchmust be able to find them; in our test it could not. - Built-in tool discoverability through
ToolSearchshould 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
- Changelog trail: 2.1.304 "Changed tool search to be on by default behind a custom
ANTHROPIC_BASE_URL; a 400 or 422 from the gateway is retried without it"; 2.1.305 fixed the case where the gateway rejects tool search (retry without). Our gateway accepts it, so the deferral activates — the subagent/headless impact above is the result. - Unrelated to issue [Bug] Entire conversation history intermittently sent twice per request (cache_read ≈ 2.00x) — a persistent variant survives resume #102788 (same environment, different bug).
Activity
axisrow commentedon Oct 24, 2026
+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 customANTHROPIC_BASE_URL(third-party 1M model) also started with only the deferred layer.Details captured from a session where this happened:
ToolSearch,WebFetch,SendMessage);Bash/Read/Write/Editwere never registered. No--allowedTools/--disallowedToolswere passed and no deny rules exist in any settings file — this is not a permissions misconfiguration.WebFetchreturned real HTTP responses), andToolSearchitself worked too — but repeated queries over the first several minutes never surfacedBash/Read/Write/Editfrom the deferred pool, matching the "deferred pool lacks core built-ins" observation above. The model visibly spent its first minutes repeatedly queryingToolSearchfor the missing core tools before giving up.--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=falsefrom this issue is the known escape hatch; not yet exercised on this particular repro.YangChen-AIdev commentedon Oct 25, 2026
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 ofToolSearch(the third run listed it under the internal nametool_search_tool_regex); Bash/Read were never present.--settingsoverrides, 3 inherited from the user-level settings file): 6/6 healthy — full direct toolset, noToolSearch.So on this machine/version the flag looks deterministic rather than probabilistic. One pitfall worth flagging for anyone reproducing: once
ENABLE_TOOL_SEARCH=falseis set in the user-level settings file, a plainclaude -pinherits it — probes meant to test the default silently become OFF probes. Explicit--settingsoverrides 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, orToolSearch-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--resumeobservation. (Our 6/6 OFF runs also cover the escape-hatch check your repro hadn't exercised yet.)