github/copilot-sdk · error · ValueError

invalid tool name: must not be empty

Error message

invalid {kind} tool name: must not be empty

What it means

Tool names registered via ToolSet.add_builtin/add_custom/add_mcp must be non-empty strings. An empty name cannot match any tool and would produce ambiguous filter entries, so _validate_tool_name raises immediately. The wildcard "*" is allowed but only as a full string, not empty.

Solutions

  1. Pass a non-empty tool name string to the add_* method
  2. If the name comes from parsing, verify the segment index (e.g. name.split(':')[-1]) before registering
  3. Fail fast at config load time: assert the tool name is non-empty before constructing a ToolSet
  4. Use ToolSet().add_builtin('*') if the intent was to match all tools of a source

Example fix

// before
prefix, _, tool = spec.partition(':')  # tool == '' when spec == 'builtin:'
toolset.add_builtin(tool)

// after
prefix, _, tool = spec.partition(':')
if not tool:
    raise ValueError(f"missing tool name in {spec!r}")
toolset.add_builtin(tool)
Defensive patterns

Strategy: validation

Validate before calling

if not isinstance(name, str) or not name:
    raise ValueError(f"{kind} tool name must be a non-empty string")

Type guard

def is_valid_tool_name(name) -> bool:
    return isinstance(name, str) and bool(name)

Try / catch

try:
    toolset.add_builtin(name)
except ValueError as e:
    logger.error(f"bad tool name {name!r}: {e}")
    raise

Prevention

When it happens

Trigger: Calling add_builtin(''), add_custom(''), or add_mcp('') — or passing a name that evaluates falsy (e.g. an empty string from an unparseable 'builtin:' prefix or a split() result) — into _validate_tool_name.

Common situations: Parsing tool identifiers like 'mcp:server:tool' with split(':') and picking the wrong segment; building names from env vars or config keys that are unset; string concatenation producing an empty suffix.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/44cadde05e46d5f0. Report an issue: GitHub.

Appendix: source

Thrown at python/copilot/_mode.py:26

"""

from __future__ import annotations

import re
from collections.abc import Iterable
from typing import TYPE_CHECKING, Any, Literal

if TYPE_CHECKING:
    from .session import MemoryConfiguration

CopilotClientMode = Literal["copilot-cli", "empty"]

_TOOL_NAME_REGEX = re.compile(r"^[a-zA-Z0-9_-]+$")


def _validate_tool_name(kind: str, name: str) -> None:
    if not name:
        raise ValueError(f"invalid {kind} tool name: must not be empty")
    if name == "*":
        return
    if not _TOOL_NAME_REGEX.match(name):
        raise ValueError(
            f"invalid {kind} tool name {name!r}: tool names must match "
            r"/^[a-zA-Z0-9_-]+$/ or be the wildcard '*'"
        )


class ToolSet:
    """Builder for source-qualified tool filter patterns.

    ``ToolSet`` accumulates entries like ``builtin:bash``, ``mcp:*``, or
    ``custom:my_tool`` for use in
    :class:`CopilotClient.create_session`'s ``available_tools`` /
    ``excluded_tools`` parameters.

    Tool classification (``builtin``/``mcp``/``custom``) is determined by the

View on GitHub (pinned to cd8cf15dc3)