Tutorials
Sofia Nieves9 min read7 views

TodoWrite No Longer Available: Claude Code's 2.1.233 Tool Gate (2026)

"TodoWrite no longer available" is not a bug. Claude Code v2.1.233 drops the five task-tracking tools on Sonnet 5 and newer unless you opt in. The gate follows the session, not the model, and the SDK opt-in line behaves differently in TypeScript and Python (2026).

Updated on September 3, 2026

Five tool tags hanging from a dark metal rail: four are faint hollow outlines and only the rightmost is solid and lit in electric blue, with an off toggle switch beside them, evoking Claude Code withholding four of five task-tracking tools until you opt in.
Five tool tags hanging from a dark metal rail: four are faint hollow outlines and only the rightmost is solid and lit in electric blue, with an off toggle switch beside them, evoking Claude Code withholding four of five task-tracking tools until you opt in.
On this page

Quick Answer (2026)

If Claude Code stopped writing a todo list, nothing is broken. As of Claude Code v2.1.233, TodoWrite, TaskCreate, TaskGet, TaskUpdate and TaskList are not provided on Opus 4.8, Sonnet 5, Fable 5, Mythos 5 or later versions of those families unless you opt in. Anthropic's stated reason is that those models track multi-step work without a written checklist, and that "the tools' definitions and reminders take up context".

The one-line fix everyone quotes:

bash
CLAUDE_CODE_ENABLE_TODO_TOOLS=1 claude

That works. Three things it leaves out matter more than the fix.

The gate does not follow the model, it follows the session. A subagent running Opus 4.7, a model that would normally get the Task tools, gets nothing if the parent session is on Sonnet 5.

The gate does not apply everywhere. Background sessions and Claude Code on the web provide the tools on every model, listed or not. The same model gives you two different tool sets depending on where it runs.

In the TypeScript Agent SDK, the copy-paste opt-in line can wipe your environment. env replaces the subprocess environment rather than merging with it. The identical-looking Python call merges. Measured below from both shipped packages.

Verified against the anthropics/claude-code changelog, Anthropic's tools reference and Agent SDK docs, @anthropic-ai/claude-agent-sdk 0.3.259 and claude-agent-sdk 0.2.152, in September 2026.

What actually changed, and in which artifacts

Anthropic The removal landed in three packages at once, and the version numbers are not interchangeable. People quoting only the CLI number send SDK users looking at the wrong changelog.

Scroll to see more

ArtifactBoundary versionEvidence
Claude Code CLI2.1.233changelog entry, quoted below
TypeScript Agent SDK0.3.233tools reference, Model availability note
Python Agent SDK0.2.139tools reference, Model availability note

The changelog entry reads, verbatim:

Todo/task-tracking tools (TaskCreate/Get/Update/List, TodoWrite) are no longer available on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, and newer models; set CLAUDE_CODE_ENABLE_TODO_TOOLS=1 to bring them back.

PyPI claude-agent-sdk 0.2.139 was uploaded to PyPI on 2026-08-14, which independently dates the boundary rather than taking a blog's word for it.

Read literally, that entry is a statement about models. That reading is what causes the two surprises in the next two sections.

The gate follows the session, not the model

Anthropic's tools reference is explicit, and this is the sentence nobody on the first page of results quotes:

Claude Code gives a subagent the tools only when your session has them, even when the subagent runs a different model.

So the decision is made once, for your session, and inherited downward. Concretely:

Scroll to see more

Parent session modelSubagent modelSubagent gets Task tools?
Sonnet 5Sonnet 5No
Sonnet 5Opus 4.7No
Opus 4.7Opus 4.7Yes
Opus 4.7Sonnet 5Yes

The two bold rows are the ones a model-based reading gets backwards. If you route a subagent to an older model specifically to get task tracking back, you will not get it. And if your parent session is on an older model, a Sonnet 5 subagent does get the tools.

Agent teams split this one step further, on display mode rather than on model. An in-process teammate follows your session the same way a subagent does. A teammate in its own split pane runs as a separate Claude Code process, so its own model decides. Same feature, two behaviours, chosen by a display setting. Without the Task tools an agent coordinates through messages instead of the shared task list, which is a real behavioural difference and not only a missing UI element. If you are wiring this up, the inheritance rules are the same ones that govern how subagents are scoped and nested.

Where the gate does not apply at all

In background sessions and in Claude Code on the web, Claude Code provides the same tools on every model, listed or not.

This is the one most likely to cost someone an afternoon. If you build a parser, a hook, or a progress display and test it interactively on Sonnet 5, you will see no task tool calls and may conclude the tools are gone for good. Run the same prompt as a background session and they are there. Any code that assumes "Sonnet 5 means no TaskCreate blocks" is wrong in exactly the environment you are least likely to test in.

Four ways to opt in, and what one of them costs you

The docs enumerate four routes, not one. Every result on the first page of Google mentions only the environment variable.

Scroll to see more

RouteWhereNote
CLAUDE_CODE_ENABLE_TODO_TOOLS=1shell, before launchprovides the tools on every model and every provider
--allowedTools TaskCreateCLI flagnaming any one of the five is enough
--tools ...CLI flagrestricts the session to the tools it names
allowedTools / tools optionsAgent SDKsame semantics as the two flags

The third row is a trap worth stating plainly. --tools is not additive. It restricts the session's built-in tools to the ones it names, so opting in this way while listing only TaskCreate removes every other built-in tool from the session. If you use it, you have to enumerate everything else you rely on alongside it.

Worth noting what is not on that list: declaring a hook matcher. The four routes above are the complete set the documentation gives, and a PreToolUse matcher on TodoWrite is not among them. So a hook written against these tools does not opt the session in; it simply never fires, silently, because the tool it matches is not present. That is an inference from the documented enumeration rather than something reproduced here, so treat it as the documented reading and not a measurement. If you maintain hooks against these tools, the relevant lifecycle is covered in the Agent SDK hooks walkthrough.

The opt-in line means two different things in the two SDKs

This is the finding worth the article. Both SDK docs give you an env option and the examples look nearly identical:

typescript
// TypeScript
options: { env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } }
python
# Python
options=ClaudeAgentOptions(env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"})

The Python version is safe. The TypeScript version is safe only because of the spread. Drop ...process.env and you spawn Claude Code with an environment containing exactly one variable.

I checked both shipped packages rather than trusting the prose.

TypeScript, @anthropic-ai/claude-agent-sdk 0.3.259, in the bundled sdk.mjs, the option is destructured with a default:

javascript
pathToClaudeCodeExecutable: a, env: c = { ...process.env }

A destructuring default applies only when the property is absent. Omit env and c becomes a copy of process.env. Pass env and c is exactly the object you passed. There is no merge step anywhere after it. The package's own type definitions state that a supplied value replaces the subprocess environment entirely rather than merging with process.env, and then name the casualties outright:

Spread process.env yourself if the subprocess still needs inherited variables like PATH, HOME, or ANTHROPIC_API_KEY. When omitted, the subprocess inherits process.env.

Python, claude-agent-sdk 0.2.152, _internal/transport/subprocess_cli.py, builds the environment explicitly:

python
inherited_env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}
process_env = {
    **inherited_env,
    "CLAUDE_CODE_ENTRYPOINT": "sdk-py",
    **self._options.env,
    "CLAUDE_AGENT_SDK_VERSION": __version__,
}

That is a real merge, with your env layered on top of the inherited environment. Two details fall out of the ordering that no summary mentions:

  • CLAUDE_CODE_ENTRYPOINT is set before your env, so you can override it.
  • CLAUDE_AGENT_SDK_VERSION is set after your env, so you cannot.
  • CLAUDECODE is deliberately stripped from the inherited environment, so an SDK subprocess does not believe it is running inside a Claude Code parent.

So the same conceptual instruction, "set this variable", is a safe additive change in Python and a destructive replacement in TypeScript. The TypeScript failure mode is nasty because it is not a crash at the env line: you lose PATH and ANTHROPIC_API_KEY, and the failure surfaces later as a missing binary or an auth error that looks unrelated to the todo tools you were trying to enable.

Two environment variables, two eras

There are two similarly-named variables in circulation and they are not alternatives.

Scroll to see more

VariableEraEffect
CLAUDE_CODE_ENABLE_TASKS=0pre-2.1.233On models that are not gated, provides TodoWrite instead of the four Task tools
CLAUDE_CODE_ENABLE_TODO_TOOLS=12.1.233 and laterOpts a gated model back in to all five

The first one is about which tracking system you get on a model that has one. The second is about whether you get one at all. Setting CLAUDE_CODE_ENABLE_TASKS=0 on Sonnet 5 does not bring the tools back. The documentation scopes that variable explicitly to "any other model", and it is absent from the list of opt-in routes for the gated ones.

I went looking for a contradiction here, on the theory that the docs still described the pre-2.1.233 world. They do not. Both variables are documented, both are current, and they answer different questions. Recording the disconfirmation because the search-result snippets make it look like a conflict.

What breaks quietly

GitHub Nothing raises an error when a tool is simply absent, which is why this shows up as confusing downstream behaviour rather than a stack trace. Skill and plugin authors hit it first: the obra/superpowers project tracked exactly this, with its skills depending on TodoWrite and the agent having no such tool available.

Things to check, in rough order of how quietly they fail:

  1. Hooks matching the five tool names. They do not fire. There is no warning.
  2. Permission rules and allowedTools entries naming them. Note the second-order effect: an allowedTools entry is itself an opt-in route, so a leftover permission rule can silently switch the tools back on in a session you expected to be clean.
  3. Skills and CLAUDE.md instructions telling Claude to keep a todo list. The instruction stays, the tool does not, and you get prose approximating a checklist.
  4. Stream parsers waiting for TaskCreate or TodoWrite tool_use blocks. They wait forever.
  5. Split-pane teammates behaving differently from in-process ones.

One extra note for anyone parsing the stream on an opted-in session, straight from Anthropic's docs: Claude Code repairs some close-but-incorrect key names before execution, mapping id or task_id to taskId and active_form to activeForm, but that repair is not reflected in the stream. The block you receive carries the raw shape the model emitted. Read those fields defensively:

javascript
const taskId = input.taskId ?? input.id ?? input.task_id;

Related: TaskCreate's assigned ID is not in the TaskCreate input at all. It arrives on the paired user message as tool_use_result, so correlating a create with its later updates means keying on tool_use_id rather than reading the ID off the call.

Checklist

  1. Is your Claude Code at 2.1.233 or later, or your SDK at TS 0.3.233 / Python 0.2.139 or later? Below those, none of this applies.
  2. Is the session model in the gated set? Opus 4.8, Sonnet 5, Fable 5, Mythos 5 and later families.
  3. Are you actually in an interactive session? Background sessions and the web get the tools regardless.
  4. If you want them back, prefer the environment variable or --allowedTools. Reach for --tools only if you are willing to enumerate every other built-in you use.
  5. In the TypeScript SDK, spread ...process.env. In Python you do not need to, and doing so is harmless.
  6. Check whether a stale allowedTools entry is opting you in when you did not mean to.
  7. If a subagent is missing the tools, look at the parent session's model, not the subagent's.

Sources

  • Claude Code changelog, anthropics/claude-code, entry under 2.1.233.
  • Anthropic tools reference, Task tool availability, for the session inheritance, background-session and opt-in routes.
  • Anthropic Agent SDK docs, Track todos, for the SDK version boundary and the key-repair note.
  • @anthropic-ai/claude-agent-sdk 0.3.259 published tarball, sdk.mjs and sdk.d.ts.
  • claude-agent-sdk 0.2.152 source distribution, _internal/transport/subprocess_cli.py.
  • Release date from the claude-agent-sdk release history on PyPI.
  • Downstream impact report: obra/superpowers issue 2177.
S

Written by

Sofia Nieves

Sofia works on agent evaluation and reliability. She writes about measuring LLM systems before and after they reach production.

Frequently asked questions

Why did Claude Code stop showing a todo list?

As of Claude Code v2.1.233, the five task-tracking tools (TodoWrite, TaskCreate, TaskGet, TaskUpdate, TaskList) are not provided on Opus 4.8, Sonnet 5, Fable 5, Mythos 5 or later versions of those families unless you opt in. Anthropic's reason is that those models track multi-step work without a written checklist and the tool definitions consume context. Nothing is broken and there is no error.

How do I get TodoWrite back?

There are four documented routes, not one. Export CLAUDE_CODE_ENABLE_TODO_TOOLS=1 before launching Claude Code; or name one of the tools in --allowedTools; or list them in --tools; or use the allowedTools or tools options in the Agent SDK. Be careful with --tools: it restricts the session to only the tools it names, so you must enumerate every other built-in you rely on.

My subagent runs an older model. Why does it still have no task tools?

Because the gate follows the session, not the model. Anthropic's tools reference states that Claude Code gives a subagent the tools only when your session has them, even when the subagent runs a different model. So a subagent on Opus 4.7 under a Sonnet 5 parent gets nothing, and a Sonnet 5 subagent under an Opus 4.7 parent does get them.

Is CLAUDE_CODE_ENABLE_TASKS the same as CLAUDE_CODE_ENABLE_TODO_TOOLS?

No. They are from different eras and answer different questions. CLAUDE_CODE_ENABLE_TASKS=0 applies to models that are not gated and selects TodoWrite instead of the four Task tools. CLAUDE_CODE_ENABLE_TODO_TOOLS=1 opts a gated model back in to all five. Setting CLAUDE_CODE_ENABLE_TASKS=0 on Sonnet 5 does not bring the tools back.

Does setting env in the Agent SDK have any side effects?

In TypeScript, yes, and it is easy to miss. The env option replaces the subprocess environment rather than merging with it, so passing only CLAUDE_CODE_ENABLE_TODO_TOOLS drops PATH, HOME and ANTHROPIC_API_KEY. Spread process.env alongside it. The Python SDK merges your env on top of the inherited environment, so the equivalent call is safe there.

Why do the task tools appear in some sessions but not others on the same model?

Background sessions and Claude Code on the web provide the same tools on every model, listed or not. So the identical model can give you two different tool sets depending on where the session runs. Code that assumes a gated model never emits TaskCreate blocks will be wrong in those environments.

Tutorials

Claude Code Subagents: How to Create, Scope, and Nest Them (2026)

The /agents creation wizard was removed in Claude Code v2.1.198, so almost every guide still ranking for this topic teaches a flow that no longer exists. Here is the current way to write, scope, invoke, and cap Claude Code subagents, with a version number on every claim. August 2026.

15 min read137
Tutorials

Claude Code Plan Mode: How It Actually Gates Your Edits (2026)

Plan mode is not a read-only state. It is a rule at step 4 of a six-step permission evaluation, which is why allow rules stop applying while you plan and why a session with bypass permissions can edit anyway. The CLI keystrokes, the settings, the Agent SDK equivalent, and a plan-then-execute pipeline in Python and TypeScript. August 2026.

14 min read120