oraios/serena · error · ValueError

Tool name is required in the hook input data

Error message

Tool name is required in the hook input data

What it means

ToolHook subclasses (e.g. pre/post-tool-use hooks) require the hook input to name the tool being triggered. __init__ reads tool_name/toolName, normalizes to lowercase, and raises ValueError if it is empty, because the hook cannot dispatch without knowing which tool fired.

Source

Thrown at src/serena/hooks.py:73

            "read",
            "diagnostics",
            "memory",
            "onboarding",
            "config",
            "list_file",
            "find_file",
            "shell",
            "dashboard",
            "restart_language_server",
        )
    )

    def __init__(self, client: HookClient):
        super().__init__(client)
        _tool_name = self._input_data.get("tool_name") or self._input_data.get("toolName", "") or ""
        _tool_name = str(_tool_name).lower().strip()
        if not _tool_name:
            raise ValueError("Tool name is required in the hook input data")
        self._tool_name = _tool_name
        raw_tool_input = self._input_data.get("tool_input") or self._input_data.get("toolInput")
        # TODO: some agents, like copilot CLI, can send a string as value for raw_tool_input
        #  Example: "tool_input":"*** Begin Patch\n*** Add File: /Users/acbdef/.copilot/session-state/08a961db-02f0-4c7c-b783-1e9818290292/files/hook-tool-test-3.txt\n+third edit tool test\n*** End Patch\n"
        #  We currently don't parse such tool input and hence don't react to it in hooks
        self._tool_input: dict | None = raw_tool_input if isinstance(raw_tool_input, dict) else None

        # only relevant in claude code at the moment, (not all events include this field; default to empty string)
        raw_permission_mode = self._input_data.get("permission_mode") or self._input_data.get("permissionMode") or ""
        self._permission_mode = str(raw_permission_mode).strip()

    @dataclass
    class OutputData:
        permission_decision: Literal["deny", "allow"]
        permission_decision_reason: str
        additional_context: str = ""

        def to_json_string(self, client: HookClient) -> str:

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Include a non-empty "tool_name" in the hook input JSON
  2. Update the agent CLI/hook client so it forwards the tool name
  3. If testing hooks manually, replicate the full payload shape the agent sends

Example fix

// before
{"session_id": "abc", "tool_input": {"path": "x.py"}}
// after
{"session_id": "abc", "tool_name": "read_file", "tool_input": {"path": "x.py"}}
Defensive patterns

Strategy: validation

Validate before calling

def validate_tool_hook(data: dict) -> str:
    name = str(data.get("tool_name") or data.get("toolName") or "").lower().strip()
    if not name:
        raise ValueError("hook input JSON must include a non-empty tool_name")
    return name

Try / catch

try:
    hook = ToolHook(client)
except ValueError as e:
    if "Tool name" in str(e):
        logging.error("Hook payload missing tool_name: check hook client schema")
    else:
        raise

Prevention

When it happens

Trigger: A hook event payload for a tool-based hook lacks tool_name/toolName or supplies an empty/whitespace string; custom hook clients that only forward tool_input.

Common situations: Custom agent integrations with incomplete hook payloads; schema drift between agent versions (toolName vs tool_name); hand-testing hook scripts with minimal JSON.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/cba316432501e8da. Report an issue: GitHub.