Claude Code Stop Hook: Force Task Completion Before Claude Stops
Use the Stop Hook to ensure Claude finishes tasks before responding. Run tests automatically, validate output, and prevent incomplete work.
Problem: Claude finishes responding but the task is incomplete. Tests are failing. Files are half-written. You ask "are you done?" and Claude says yes, but the build is broken.
Quick Win: Add this Stop hook and Claude cannot stop until tests pass:
Now Claude literally cannot stop responding until tests pass.
How the Stop Hook Works
The Stop hook fires every time Claude finishes a response. You can:
- Allow stopping - Exit 0, Claude stops normally
- Block stopping - Return
{"decision": "block", "reason": "..."}, Claude continues working - Run validations - Execute tests, checks, or validations automatically
The Payload
The stop_hook_active flag is critical. When true, Claude is already in a "forced continuation" state from a previous block. Always check this to prevent infinite loops.
Pattern 1: Test Gate
Block Claude until all tests pass:
Pattern 2: Build Validation
Ensure the project builds before Claude stops:
Pattern 3: Lint Check
No stopping with lint errors:
Pattern 4: Task Completion Marker
Check if a specific task was completed:
Create the marker when starting work:
Delete it when done:
Preventing Infinite Loops
The stop_hook_active flag prevents loops. Here's why it matters:
Always check the flag first:
Combining Multiple Checks
Chain validations in a single hook:
When to Use Stop Hooks
Good use cases:
- Ensuring tests pass before "task complete"
- Validating builds succeed
- Checking lint/type errors
- Custom completion criteria
Bad use cases:
- Long-running operations (60 second timeout)
- Network-dependent checks (flaky)
- Blocking on user input (can't interact)
Configuration
Add to .claude/settings.json:
Multiple hooks run in parallel. If any returns block, Claude continues.
Debugging
Infinite loop?
- Check that you're reading
stop_hook_activecorrectly - Add logging:
echo "stop_hook_active: $stop_hook_active" >> ~/.claude/stop-debug.log
Hook not blocking?
- Verify JSON output format:
{"decision": "block", "reason": "..."} - Check exit code is 0 (not 2 for blocking)
Tests timing out?
- 60 second hook timeout
- Run subset of tests or increase efficiency
The "Ralph Wilgum" Pattern
Named after a community technique, this pattern uses Stop hooks to create persistent task loops:
- Create task marker at session start
- Stop hook blocks until marker removed
- Claude must explicitly complete task to remove marker
- No accidental "I'm done" when work is incomplete
This transforms Claude from "best effort" to "guaranteed completion."
Next Steps
- Set up the main Hooks Guide for all hook types
- Configure Context Recovery to survive compaction
- Learn Skill Activation for automatic skill loading
- Explore Permission Hooks for auto-approval
Last updated on