rohitg00/ai-engineering-from-scratch · error · ValueError
name must be a non-empty string
Error message
name must be a non-empty string
What it means
_call_tool validates params['name'] before anything else: it must be a non-empty string. The guard rejects wrong-shape requests before registry lookup, mirroring JSON-RPC -32602 invalid params. It is purely a type/emptiness check, not a lookup failure.
Source
Thrown at certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py:311
raise ValueError("name must be a string")
if name not in self.prompts:
raise ValueError("unknown prompt")
return self._complete(
messages=[
{
"role": "user",
"content": {"type": "text", "text": self.prompts[name]},
}
]
), []
raise LookupError(f"Method not found: {method}")
def _call_tool(
self, params: dict[str, Any], metadata: dict[str, Any]
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
name = params["name"]
if not isinstance(name, str) or not name:
raise ValueError("name must be a non-empty string")
tool = self.tools.get(name)
if tool is None:
raise ValueError("unknown tool")
arguments = tool.validate_arguments(params.get("arguments", {}))
if name == "prepare_review":
return self._prepare_review(params, metadata, arguments), []
token = metadata.get("progressToken")
notifications: list[dict[str, Any]] = []
if token is not None:
if not isinstance(token, (str, int)) or isinstance(token, bool):
raise ValueError("progressToken must be a string or integer")
notifications = [
self._progress(token, 0, 1, "starting"),
self._progress(token, 1, 1, "complete"),
]
value = tool.handler(arguments)
return self._complete(View on GitHub (pinned to 39ea8a1c6d)
Solutions
- Pass the tool name as a non-empty string, e.g. {"name": "prepare_review", "arguments": {...}}
- Validate the name type at the client boundary before sending the request
- Fetch tools/list and copy an advertised name verbatim
Example fix
# before
params = {"name": "", "arguments": {}}
# after
params = {"name": "prepare_review", "arguments": {"topic": "api"}} Defensive patterns
Strategy: type-guard
Validate before calling
name = payload.get("name")
if not isinstance(name, str) or not name:
raise ValueError("tools/call name must be a non-empty string")
server.exchange("tools/call", {"name": name, "arguments": payload.get("arguments", {})}) Type guard
def is_nonempty_str(value: object) -> bool:
return isinstance(value, str) and len(value) > 0 Try / catch
try:
result = server.exchange("tools/call", params)
except ValueError as exc:
respond_error(request_id, -32602, str(exc)) Prevention
- Validate tool name type and emptiness at the client boundary
- Never build tool names from untyped user input
- Keep a typed ToolCall dataclass so name is a string by construction
When it happens
Trigger: tools/call with params like {"name": 123}, {"name": ""}, {"name": ["prepare_review"]}, or a name key holding None.
Common situations: Client builds the tool name from unvalidated user input or config; an SDK serializes name as a non-string; test fixtures pass an empty string to probe validation.
Related errors
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/ca7e250349e3abfd.
Report an issue: GitHub.