PreToolUse agent hooks fail with 'Messages are required' despite schema requiring only 'prompt'
Summary
PreToolUse hooks of type: "agent" have a schema/runtime contract mismatch. The settings.json JSON schema requires prompt: string and explicitly forbids messages (via additionalProperties: false), but at runtime the harness rejects the hook with Failed to run: Messages are required for agent hooks. This is a bug. — the error message itself flags the defect.
Net effect: every PreToolUse hook of type: "agent" is a silent no-op on its matching tool call. A git commit review guardrail configured this way never runs.
Steps to reproduce
- Add to project
.claude/settings.json:
``json``
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "agent",
"if": "Bash(git commit*)",
"prompt": "Review the staged changes before committing.",
"model": "claude-sonnet-4-6",
"timeout": 600
}
]
}
]
}
}
- Inside a Claude Code session against that repo, run
git commit -m "test"via theBashtool. - Observe the error surfaced by the harness:
````
PreToolUse:Bash hook error
Failed to run: Messages are required for agent hooks. This is a bug.
- Note that the
git commititself succeeds — the agent hook is bypassed entirely.
Expected vs actual
Expected: the harness wraps the prompt string into whatever internal messages shape the agent runner requires, executes the review agent against the repo, and returns its allow/block decision to the pre-commit gate (matching the behaviour documented for agent-type hooks).
Actual: the runtime validator demands a messages field that the schema does not allow. The hook aborts, the commit proceeds unreviewed, and the only user-visible signal is a one-line harness warning.
Schema vs runtime (copied from the live schema)
{
"type": "agent",
"required": ["type", "prompt"],
"additionalProperties": false,
"properties": {
"type": { "const": "agent" },
"prompt": { "type": "string", "description": "Prompt describing what to verify..." },
"if": { "type": "string" },
"timeout": { "type": "number" },
"model": { "type": "string" },
"statusMessage": { "type": "string" },
"once": { "type": "boolean" }
}
}
Any attempt to work around the runtime by adding messages: [{role, content}] is rejected at settings-load time by the schema above, so users have no local-config escape hatch.
Impact
PreToolUseagent hooks targetingBash(git commit*)(and similar) cannot enforce pre-commit review in any repo.- The failure is silent in the sense that it does not block the triggering tool call; users only notice when they re-read the scroll or audit the review trail.
- Any security-sensitive guardrail (
/security-review,/deep-review, policy gates) wired up viatype: "agent"hooks is inert.
Environment
- Claude Code CLI (Opus 4.7 1M context channel, but the schema/runtime code path is model-agnostic)
- macOS 25.4.0, bash shell
- Repro independent of MCP server configuration.
Suggested fix
Make the runtime accept prompt and internally construct the messages array it needs, so the schema and the validator agree. Alternatively: widen the schema to accept messages and keep prompt as a backwards-compatible convenience — and update the docs to reflect the chosen shape.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗