Field note
Claude Code Hook Cookbook: Patterns From Production
A deep-dive explainer on Claude Code Hook Cookbook: Patterns From Production: methodology, historical context, worked examples with real numbers, and common pit
PreToolUse Rewrite: Streamlining Input Validation
The PreToolUse hook allows developers to intercept a user’s raw prompt before the model runs any LLM calls. By validating and normalizing input at this stage, teams can enforce schema constraints, remove disallowed tokens, and short‑circuit malformed requests without incurring a token cost. The hook receives the original prompt string, the context metadata, and a mutable result object that can be modified. The pattern typically follows a pipeline: parse the prompt into a JSON‑like structure, apply validation rules, and either replace or augment the prompt before passing control to the next stage. In practice this reduces downstream errors that would otherwise trigger costly retries or error handling logic inside the model.
When the PreToolUse rewrite is configured, the entire request lifecycle becomes deterministic. Validation failures can be surfaced immediately with a custom error message, and successful passes are forwarded to the model unchanged. This early exit strategy eliminates the need for the LLM to parse and reject malformed input, thereby saving on token usage and improving latency. The hook’s configuration can be expressed declaratively in a JSON or YAML file, making it easy for operations teams to update validation rules without redeploying code. Additionally, the same rewrite logic can be leveraged for multi‑step workflows, allowing subsequent hooks to assume well‑formed input without duplicating checks.
The benefit to data‑centric engineering teams is clear: reduced model token consumption, consistent error handling, and tighter control over the prompt format. For developers focused on feature delivery who rely on the model to handle any input gracefully, the PreToolUse hook offers a pragmatic compromise between flexibility and safety. Those working on purely experimental prototypes might skip the hook to preserve raw input variability.
The Pixelmojo article discusses six production patterns, including the PreToolUse rewrite.
The Pixelmojo article highlights six production patterns, providing a concise taxonomy for hook integration. This is sourced from the Pixelmojo blog: Claude Code Hooks: 6 Production Patterns (2026).
Exit code semantics matter. Exit 0 means success (proceed). Exit 2 means block the action.
This principle is articulated in the Claude Code Hooks Tutorial: 5 Production Hooks From Scratch by Blake Crosley. Claude Code Hooks Tutorial
PostToolUse Error Analysis: Diagnosing Execution Failures
When a Claude Code Hook is executed, the PostToolUse phase receives the raw tool output and the system’s internal state snapshot. The hook’s responsibility is to inspect this data, determine if the tool run succeeded, and decide whether to retry, abort, or transform the output before resubmission. Failure detection begins with parsing the tool’s exit code, parsing structured logs, and validating output against a schema that the prompt expected.
The most common failure modes are: an exit code other than zero, a malformed JSON payload, or a semantic mismatch between the output and the prompt’s expected intent. Hooks can log the raw payload, capture stack traces from the underlying tool, and surface diagnostic hints. For example, a Git tool that reports error: unable to push to 'origin' can trigger a hook that extracts the error string and maps it to a retry strategy or a human‑readable error message.
Here’s a minimal Python hook that logs failures and aborts the conversation if the tool returns a non‑zero exit code:
def post_tool_use(context):
result = context.tool_output
if result.get("exit_code", 0) != 0:
context.log("Tool error:", result.get("error", "unknown"))
context.abort("Tool execution failed")
The hook library can be extended with pattern matching on the error string, enabling automatic retry logic for transient network errors while flagging destructive commands for manual review.
The cookbook includes four distinct hook examples that cover common failure scenarios, such as handling Git errors, parsing JSON, and validating file system operations.
The cookbook contains four hook examples that illustrate different failure detection patterns. Each example demonstrates a unique error signature and recovery strategy, sourced from the Steve Kinney’s Claude Code Hook Cookbook.
These examples emphasize that even a small set of patterns can cover the majority of operational failures. The pattern set also documents dangerous command patterns that should never be executed in a production environment.
“Blocked patterns include rm -rf / (recursive deletion from root), git push , force main and git push -f main (force pushing to the main branch), git reset , hard (destroying uncommitted work), DROP TABLE (accidental database destruction), and fork bombs.” Claude Code Hooks Tutorial: 5 Production Hooks From Scratch by Blake Crosley (source)
By systematically capturing error indicators and applying deterministic patterns, engineers can reduce the number of manual interventions required to debug tool failures. This disciplined approach also improves auditability, as each failure path is logged and traceable back to the exact hook logic that handled it.
UserPromptSubmit Injection: Enhancing Dynamic Prompting
The UserPromptSubmit hook enables a prompt template to be updated on each request cycle. It listens for the UserPromptSubmit event emitted by Claude Code when a user submits a prompt, then injects new dynamic data before the prompt reaches the LLM. The injection occurs in a thin wrapper around the original prompt text, preserving the user’s intent while enriching the context with runtime variables such as timestamps, session identifiers, or custom flags that control tool behavior.
To implement this pattern, the hook reads the tool’s JSON input from standard input and extracts .tool_input.file_path with jq. That stdin object is the only place the file path exists, since Claude Code sets no per‑tool environment variables. The extracted path can then be used to load supplementary files, parse metadata, or conditionally alter the prompt template. For example, the hook can prepend a file header or append a checksum, allowing downstream tools to validate the input without re‑reading the file. This technique reduces redundant I/O and keeps the prompt payload small.
The disler/claude-code-hooks-mastery GitHub repository has 32 stars – a tangible indicator of community uptake for the hook patterns documented in the tutorial series.
The injection logic is straightforward in a shell script:
# user_prompt_submit.sh
#!/usr/bin/env bash
set -euo pipefail
# Read JSON from stdin
input=$(cat)
# Extract the file path
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# Load file content
file_content=$(<"$file_path")
# Build new prompt
new_prompt="You are an assistant that references ${file_path}:\n\n${file_content}\n\nUser: $1"
# Output the modified prompt to stdout
echo "$new_prompt"
This script can be registered as a hook in the claude-code-hooks-mastery configuration. When the user submits a prompt, the hook receives the original prompt text, augments it with the file content, and forwards it to Claude for generation. The approach keeps the hook logic isolated from the main application code, making it reusable across projects and easy to test in isolation.
The hook reads the tool’s JSON input from stdin and extracts .tool_input.file_path with jq – that stdin object is the only place the file path exists, since Claude Code sets no per‑tool environment variables. Claude Code Hooks Tutorial: 5 Production Hooks From Scratch by Blake Crosley
By injecting data at submission time, engineers can maintain a clean separation between prompt design and runtime context, allowing for rapid iteration and modular feature expansion without touching the core LLM workflow.
SessionStart Context Priming: Reducing Redundant Tokens
When a Claude session begins, the model receives a system prompt that defines its behavior and any domain‑specific instructions. In many production pipelines the same boilerplate text appears in every request, inflating the token count and increasing latency. SessionStart Context Priming moves that repeated information out of the per‑request payload and into a persistent session state, allowing subsequent calls to focus only on the variable user input.
The mechanism works by sending a single initialization call that contains the full system prompt and any static context the model will need. The response includes a session identifier that the client stores. Future calls include only the session identifier and the new user message. Internally Claude re‑applies the primed context without retransmitting it, so the token budget is spent on fresh content. This approach also simplifies prompt management because updates to the static context require only one re‑initialization rather than a sweep of all downstream services.
# Initialize a session with the full system prompt
session = client.start_session(
system_prompt="""
You are a helpful coding assistant.
Follow the company's style guide for Python.
Use only standard library modules.
""")
# Subsequent calls only send the session ID and user query
response = client.chat(
session_id=session.id,
user_message="Write a function that merges two dicts.")
print(response.content)
Teams that run high‑throughput conversational workloads benefit from this pattern because it cuts per‑request token usage by the length of the static prompt, which can be several hundred tokens. Services that need low latency or operate under strict token limits should adopt SessionStart Context Priming. Projects with occasional, single‑shot calls or those that rely on highly dynamic system prompts may find the extra session management overhead unnecessary and can continue using the traditional full prompt per request. The trade‑off is minimal: the only added complexity is maintaining session lifecycles, which most SDKs already expose.
Pattern Failure Modes: Common Pitfalls and Fixes
Production code that wraps Claude often collapses under subtle mismatches between the hook’s expectations and the surrounding runtime. The most frequent failures arise from assumptions about input shape, tool return values, dynamic prompt assembly, and session state. When a pattern is copied without auditing these assumptions, the hook can generate malformed requests, waste tokens, or return opaque errors that are hard to debug. Recognizing the underlying cause makes it possible to apply a targeted fix rather than resorting to blanket retries.
The PreToolUse rewrite pattern is designed to validate and reshape user input before a tool call. A common pitfall is omitting required fields in the validation schema, which lets the hook forward incomplete payloads to the tool. The downstream service then rejects the request with a generic error that the hook logs as “tool failure”. Because the validation step does not raise early, the error appears later in the pipeline, obscuring the root cause. The fix is to define a strict schema, preferably using a library such as pydantic, and to abort the hook with a clear message when validation fails. Early exit prevents unnecessary tool invocation and produces deterministic logs.
PostToolUse error analysis often assumes that a successful HTTP status means the tool’s business logic succeeded. In practice, tools may return a success code while embedding an error message in the payload. Hook implementations that simply forward the payload to the next stage will propagate the hidden failure, leading to downstream confusion. A robust fix checks both the transport status and the payload’s error field, logs any discrepancy, and optionally retries with exponential back‑off. Adding a small retry wrapper around the tool call isolates transient glitches without masking persistent issues.
UserPromptSubmit injection patterns encourage dynamic prompt construction by concatenating user data directly into a template. This approach is vulnerable to prompt injection, where malicious input alters the intended instruction, and to token overflow when the concatenated string grows unchecked. The remedy is to use a safe templating engine that escapes user variables, and to enforce a maximum token length before submission. Truncating or summarizing user input when it exceeds the limit keeps the request within the model’s context window.
SessionStart context priming often forgets to clear or version prior session variables, causing stale context to bleed into new interactions. The result is that Claude may answer based on outdated assumptions, producing irrelevant or contradictory output. The fix is to explicitly reset the session dictionary at the start of each new conversation, or to embed a version token in the priming prompt that forces Claude to treat the session as fresh. By resetting state deterministically, developers avoid hidden state leakage and maintain consistent behavior across deployments.
Quantifying Token Savings: Metrics and Benchmarks
Measuring the efficacy of Claude Code hooks requires a shift from qualitative observation to quantitative analysis. While a developer might notice a smoother interaction after implementing a SessionStart context priming pattern, production-grade workflows demand hard data to justify the added complexity of the hook layer. The primary metric for evaluating these patterns is the reduction in total tokens consumed per successful task completion. This is not merely about reducing the size of a single prompt; it is about minimizing the cumulative token overhead across an entire multi-turn session.
Engineers should track three specific categories of metrics to gain a complete picture of hook performance. First, monitor the input token delta, which measures the difference between the raw user prompt and the primed prompt generated by the hook. Second, track the tool-use efficiency ratio. This is the ratio of successful tool calls to the total number of tool calls initiated. A high-quality PreToolUse rewrite should increase this ratio by catching validation errors before they reach the model. Third, measure the latency-to-token ratio. While hooks add a small amount of execution time, the goal is to ensure that the reduction in model processing time (due to smaller, more focused contexts) outweighs the overhead of the hook execution itself.
To implement this, developers can log the token counts provided by the Anthropic API for every request and response. By tagging these logs with the specific hook pattern used, you can perform a comparative analysis. For example, you might compare a baseline session against a session utilizing the SessionStart priming pattern.
def calculate_savings(baseline_tokens, hooked_tokens):
savings = baseline_tokens - hooked_tokens
percentage = (savings / baseline_tokens) * 100
return savings, percentage
# Example usage
baseline = 1500
hooked = 1100
saved, pct = calculate_savings(baseline, hooked)
print(f"Saved {saved} tokens ({pct:.2f}%)")
Those focused on cost optimization and high-throughput agentic workflows should prioritize these benchmarks. Teams running large-scale automated refactoring or continuous integration agents will see significant financial impact from even marginal percentage improvements. Conversely, developers working on low-frequency, highly interactive manual coding tasks may find the overhead of rigorous metric tracking unnecessary.
Integrating Patterns: A Cohesive Production Workflow
Effective production use of Claude Code Hooks requires a systematic layering of the documented patterns. The workflow typically begins with a SessionStart that injects context priming tokens, reducing redundant token generation in subsequent calls. Following this, a PreToolUse Rewrite step validates input shape before any tool invocation, ensuring that malformed payloads are rejected early. When a tool does execute, the PostToolUse Error Analysis hook captures the raw response, classifies failure modes, and routes them to a logging sink for later review. Dynamic prompting is then refined by a UserPromptSubmit Injection that appends domain‑specific suffixes based on the error classification, allowing the model to adapt its output without manual edits. This loop of validation, execution, error capture, and prompt enrichment creates a feedback cycle that can be scripted in a single Bash pipeline. For example:
#!/usr/bin/env bash
# Initialize session with context priming
rtk claude session start , priming-tokens 120
# Validate input before tool use
rtk claude rewrite , validate-input
# Execute tool and capture errors
rtk claude execute , tool git-status , error-handler log_errors.py
# Inject refined prompt based on error type
rtk claude prompt submit , suffix "_retry" , on-error
# Run final synthesis
rtk claude synthesize , output report.md
The pattern failure modes documented in the cookbook highlight common pitfalls such as over‑reliance on default error codes or neglecting to reset the session state after a failure. By explicitly resetting the session context after each error, the pipeline avoids token drift and maintains predictable cost metrics. Quantifying token savings can be done by comparing baseline runs with the instrumented workflow; the reduction typically ranges from 15 % to 25 % depending on the complexity of the task. The integration of these patterns yields a reproducible pipeline that can be version‑controlled and deployed across teams. The approach aligns with the principles outlined in the Anthropic documentation and can be extended with custom hooks as needed.
Monitoring can be achieved by piping the error handler output to a metrics collector that records latency and token count per iteration. This data feeds into a dashboard that highlights outliers and suggests adjustments to the priming token budget. When scaling across multiple repositories, the same hook definitions can be shared via a central configuration repository, ensuring consistent behavior. The cohesive workflow thus moves from static input checks to dynamic error response handling, culminating in a self‑correcting execution loop that reduces manual intervention. Adopting this integrated pattern set enables teams to maintain high reliability while keeping operational overhead low.