`allowed_tools=None` raises `TypeError` deep in the transport instead of being treated as unset
Describe the bug
Passing allowed_tools=None explicitly is accepted silently by ClaudeAgentOptions (plain dataclass, no validation), then crashes far from the cause — in one of two places depending on configuration:
- At query construction (only when
can_use_toolis set) —types.py,_warn_if_can_use_tool_shadowed:
allowed_tools = options.allowed_tools
if options.skills == _SKILLS_ALL and "Skill" not in allowed_tools: # TypeError: argument of type 'NoneType' is not iterable
allowed_tools = [*allowed_tools, "Skill"]- At connect time (always) —
_internal/transport/subprocess_cli.py,_apply_skills_defaults:
allowed_tools: list[str] = list(self._options.allowed_tools) # TypeError: 'NoneType' object is not iterableReproduced on the latest release (0.2.152) and on current main (both lines unchanged).
To Reproduce
from claude_agent_sdk import ClaudeAgentOptions
from claude_agent_sdk._internal.transport.subprocess_cli import SubprocessCLITransport
options = ClaudeAgentOptions(allowed_tools=None) # accepted silently
transport = SubprocessCLITransport(prompt="hi", options=options)
transport._apply_skills_defaults() # TypeError: 'NoneType' object is not iterableWith can_use_tool set it crashes even earlier, at _warn_if_can_use_tool_shadowed(options).
Expected behavior
None is the natural "unset" sentinel for config-driven callers (e.g. options assembled from JSON/YAML, where an absent key yields None). The sibling fields already accept it:
tools: list[str] | ToolsPreset | None = None
skills: list[str] | Literal["all"] | None = None
setting_sources: list[str] | None = None
allowed_tools: list[str] = field(default_factory=list) # the only one that crashes on Noneallowed_tools=None should behave like the default (no --allowedTools flag), consistent with tools / skills / setting_sources.
Environment
- claude-agent-sdk-python: 0.2.152 (latest release) and current
main - Python 3.13 (Windows), but the crash is platform-independent
Additional context
Two adjacent notes:
- The field annotation says
list[str](soNoneis technically type-invalid), but the dataclass does not validate at construction — the caller gets no early, actionable error, only aTypeErrorfrom transport internals at connect time. - The related empty-list semantics fix from #523 was closed pointing at PR #638, but #638 is still unmerged and
mainretains the old truthiness checks — so both that issue's concern and this crash are live onmain. This report is narrowly about theNonecrash; happy to keep the[]-vs-unset discussion in #523.
I have a fix ready (widen the annotation to list[str] | None, normalize to [] at the two consumption sites, regression tests for both crash sites) and will open a PR referencing this issue.
Source: anthropics/claude-agent-sdk-python