[BUG] Task tools (TaskCreate/TaskGet/TaskList/TaskUpdate) silently disabled on Opus 4.8 / Sonnet 5 / Fable 5 by model-id gate `tengu_vellum_ash` — no error, no override, ToolSearch reports them as nonexistent

Open 💬 6 comments Opened Jul 8, 2026 by hanuele

Summary

As of v2.1.204, the four Task tools — TaskCreate, TaskGet, TaskList, TaskUpdate — are silently unavailable in sessions running claude-opus-4-8, claude-sonnet-5, or claude-fable-5. They are not deferred, not permission-denied, and produce no error or warning: they are never registered, so ToolSearch cannot see them at all.

The cause is a model-id check against a GrowthBook flag named tengu_vellum_ash.

This may well be intentional. If so, this issue is a request to make it observable — right now there is no way for a user or an agent to discover why the tools vanished, and skills/subagents that declare them silently receive a smaller toolset. If it is not intentional, it is a regression affecting every user on the three newest models.

Reproduction

$ claude --version
2.1.204 (Claude Code)

# gated model -> tools absent
$ claude --model opus -p 'Call ToolSearch with query "select:TaskGet" max_results 3. Reply ONLY YES if a TaskGet schema came back, NO if not.'
NO

# ungated model, SAME machine, SAME binary, SAME MCP servers -> tools present
$ claude --model haiku -p 'Call ToolSearch with query "select:TaskGet" max_results 3. Reply ONLY YES if a TaskGet schema came back, NO if not.'
YES

In the gated session, ToolSearch with select:TaskGet returns the literal string No matching deferred tools found. A disabled tool never enters the deferred registry, so this is indistinguishable from the tool not existing.

Root cause

All of the following is read directly out of the shipped bin/claude.exe (v2.1.204, npm @anthropic-ai/claude-code).

Tool registration — identical for all four:

userFacingName(){return"TaskGet"},shouldDefer:!0,isEnabled(){return aR()&&!zY()}

The two predicates:

function aR(){ if (Bc(process.env.CLAUDE_CODE_ENABLE_TASKS)) return !1; return !0 }

function Bc(e){
  if (e === void 0) return !1;
  if (typeof e === "boolean") return !e;
  let t = String(e).toLowerCase().trim();
  return ["0","false","no","off"].includes(t);
}

function zY(){
  try {
    let e = Ze("tengu_vellum_ash", []);
    if (!Array.isArray(e) || e.length === 0) return !1;
    let t = Bi();                                   // resolves the MODEL ID
    return e.some((r) => r.length > 0 && t.includes(r));
  } catch { return !1 }
}

function Bi(){ let e = gQ(); if (e !== void 0 && e !== null) return ii(e); return uE() }

The flag value, as cached locally in .claude.json:

"tengu_vellum_ash": ["claude-opus-4-8", "claude-sonnet-5", "claude-fable-5"]

With CLAUDE_CODE_ENABLE_TASKS unset, aR() returns true. A model id of claude-opus-4-8[1m] substring-matches the entry claude-opus-4-8, so zY() returns true, so isEnabled() returns false, and the tool is never registered.

Note the matching is t.includes(r), i.e. substring, not equality — so the 1M-context variants (claude-opus-4-8[1m]) are caught by the base-id entry.

Evidence that this is the mechanism, and not something else

Exactly four tools in the binary carry the isEnabled(){return aR()&&!zY()} predicate: TaskCreate, TaskGet, TaskList, TaskUpdate — and exactly those four are the ones missing. TaskOutput and TaskStop do not use that registration form, and both remain present and callable in the same gated session.

This rules out a tool-count / schema-budget overflow (which would drop an arbitrary tail): the drop set coincides perfectly with a source-level predicate while 200+ later-registered MCP tools remain resolvable via ToolSearch.

There is no way for a user to re-enable the tools

CLAUDE_CODE_ENABLE_TASKS can only disable. Per Bc() above, it matches only "0"/"false"/"no"/"off"; setting it to 1 leaves aR() at true — which it already is — and has no effect on zY(). No environment variable clears zY().

This is a behavioural change from earlier builds. In v2.1.70 the same four tools registered as:

userFacingName(){return"TaskGet"},shouldDefer:!0,isEnabled(){return fK()}

function fK(){ if (f$(process.env.CLAUDE_CODE_ENABLE_TASKS)) return !0; return !xf() }
function xf(){ return !C$.isInteractive }

i.e. CLAUDE_CODE_ENABLE_TASKS was an opt-in force-enable that overrode the interactive/TTY gate (the gate reported in #23874). In v2.1.204 the variable's semantics are inverted to opt-out only, and the interactive gate has been replaced by the model gate. So the one escape hatch users previously had was removed in the same change that introduced the condition it would have escaped.

v2.1.70 contains zero occurrences of tengu_vellum_ash — and also zero occurrences of claude-opus-4-8 or claude-sonnet-5. There appears to be no released build in which these models and these tools coexist, so downgrading is not a workaround: it trades the model for the tools.

Why this is worth fixing even if the gate is intentional

The failure is silent and undiscoverable:

  1. No error, no warning, no log line. The tools simply are not there.
  2. ToolSearch returns No matching deferred tools found — identical to querying a tool that does not exist in the product.
  3. Subagents and custom agent definitions that declare TaskGet / TaskCreate in their tools: frontmatter silently receive a smaller toolset, with no diagnostic.
  4. Any skill or workflow that assumes TaskCreate exists can partially execute. In our case a skill guarantees "external ticket mutations only fire after the local mirror task is created" — an atomicity property that assumes TaskCreate is callable. A silently-absent tool turns that guarantee into a half-applied state.
  5. Tool registration is evaluated once per process, at session start, using the session model. A subagent spawned with model: haiku from a gated Opus session still inherits the gated registry — so "just use a different model for that subagent" does not work either. Only a fresh top-level session on an ungated model restores them.

Points 3–5 mean the blast radius is larger than "four tools missing": it silently degrades agent and skill behaviour in ways that look like logic bugs elsewhere.

Requests

Whichever applies:

  • If the gate is unintentional (e.g. a flag that was meant to be cleared when these models shipped): please clear tengu_vellum_ash, or scope it to the models it was actually meant to cover.
  • If the gate is intentional: please make it observable and overridable —
  • emit a warning or a /doctor line when Task tools are suppressed by model gating, naming the reason;
  • have ToolSearch distinguish "disabled on this model" from "no such tool";
  • restore an env escape hatch (CLAUDE_CODE_ENABLE_TASKS=1 forcing enable, as in ≤2.1.70), or document that none exists;
  • document the model-support matrix for Tasks, since the TeamCreate system-prompt text still instructs models to "Create tasks using the Task tools (TaskCreate, TaskList, etc.)" — the same documentation mismatch reported in #23816, now reachable by a different route.

Environment

  • Claude Code 2.1.204 (npm @anthropic-ai/claude-code, current latest at time of writing; published 2026-07-08T00:04:57Z)
  • Windows 11 Pro 26200, PowerShell 7 + Git Bash
  • Models: claude-opus-4-8[1m] (gated), claude-haiku-4-5-20251001 (not gated)
  • Reproduced independently on two separate Windows machines with different MCP server sets and different config roots, which is consistent with a server-side flag + model check and inconsistent with any per-machine cause.
  • Compared against the locally-cached 2.1.70 build (published 2026-03-06), which lacks the gate entirely.

Related but distinct

  • #23874 — Task tools disabled in the VSCode extension by the isTTY / interactive check. That is the xf() / !isInteractive gate, which no longer exists in the 2.1.204 enable path. Different mechanism.
  • #23816 — Task tools referenced in TeamCreate docs but absent at runtime, at v2.1.32–34. Same symptom, and the documentation mismatch it reports is still live, but the 2.1.204 cause is the model gate, not that one.

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗