Hooks debuggen wenn nichts feuert
Zehn Schritte um herauszufinden warum dein Claude Code Hook nicht aktiv wird. Vom JSON-Tippfehler bis zum Matcher-Pattern.
Du hast den Hook eingerichtet. Du editierst ein File, du startest Bash, du gibst einen Prompt ein. Und es passiert nichts. Kein Block, kein Log, kein Hinweis dass dein Hook ueberhaupt existiert. Das ist der häufigste Fehlerfall bei Hooks, und meistens liegt es an einer von zehn Sachen. Ich gehe die der Reihe nach durch.
Voraussetzung ist dass du den Hook-Playbook (Hooks gegen Halluzinationen) schon einmal durchgemacht hast und einen Hook in ~/.claude/settings.json stehen hast. Falls nicht, fang dort an und komm hierhin zurück wenn er nicht feuert.
1. Settings-File auf JSON-Validitaet prüfen
Erster Punkt, leider auch der häufigste. Ein fehlendes Komma, eine zuviele schliessende Klammer, ein Smart-Quote statt eines normalen Anfuehrungszeichens. Claude Code logged das nicht laut, es ignoriert die Hook-Sektion still.
cat ~/.claude/settings.json | jq .
Wenn jq einen Parse-Fehler wirft, hast du deinen Fehler. Wenn jq durchlaeuft, geh weiter. Auf macOS und Linux liegt settings.json immer in ~/.claude/, unter Windows im AppData-Roaming-Pfad.
2. Hook-Pfad muss absolut sein
Relative Pfade in der Command-Property funktionieren nicht zuverlaessig, weil Claude Code aus unterschiedlichen Working-Directories gestartet wird. Schreib den vollen Pfad rein, mit $HOME aufgeloest:
{
"type": "command",
"command": "/home/du/.claude/hooks/read-before-edit.sh"
}
Nicht ./hooks/read-before-edit.sh und nicht ~/.claude/hooks/read-before-edit.sh ohne Shell-Expansion. Tilde wird in JSON nicht expandiert.
3. Hook-Script muss executable sein
Das vergesse ich selber regelmäßig. Das Script existiert, der Pfad stimmt, aber es ist nicht executable und Claude Code wirft im Hintergrund einen Permission-Denied den du nirgendwo siehst.
chmod +x ~/.claude/hooks/*.sh
ls -la ~/.claude/hooks/
Die x-Flags müssen stehen. Wenn du dein Script frisch erstellt hast, fehlt das oft.
4. Matcher matched das Tool wirklich
Hooks haben einen Matcher der per Regex auf den Tool-Namen geht. Edit|Write|MultiEdit ist ein anderer Tool-Set als mcp__filesystem__write_file. Wenn dein Hook auf File-Edits abzielt aber dein Modell gerade über MCP schreibt, feuert der Hook nicht. Stand Mai 2026 sind die nativen Tools Read, Write, Edit, MultiEdit, Bash, Glob, Grep. Alles mit mcp__ Praefix ist MCP, eigener Matcher nötig.
Testen kannst du das per Regex-Tool deiner Wahl, oder einfach durch einen Bypass-Hook der auf .* matched und nur logged welche Tools im Lauf vorkommen. Das führt direkt zu Punkt 7.
5. Bei PreToolUse zählt der Exit-Code
PreToolUse-Hooks blockieren die Tool-Ausführung wenn der Exit-Code ungleich 0 ist. Wenn dein Script intern crasht aber mit exit 0 endet (z.B. durch ein set +e am Anfang oder weil die letzte Anweisung erfolgreich war), denkt Claude Code dass alles in Ordnung ist und führt das Tool aus.
Pattern das funktioniert: am Anfang set -e, dann gezielte Checks mit explizitem exit 1 und einer Stderr-Message. Beispiel:
#!/usr/bin/env bash
set -e
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ -z "$FILE" ]]; then
exit 0 # nicht unser Tool
fi
if ! grep -q "$FILE" "$CLAUDE_SESSION_LOG"; then
echo "Blocked: $FILE wurde noch nicht gelesen" >&2
exit 1
fi
Stderr wird in der Claude Code Konsole sichtbar, stdout wandert in die Tool-Output-Pipeline. Verwechselst du die beiden, denkt Claude Code das Blocking-Message sei ein Tool-Ergebnis.
6. Stdin enthaelt das Tool-Input als JSON
Claude Code uebergibt den kompletten Tool-Call als JSON auf stdin. Wenn dein Script kein cat oder read macht, sieht es das Input nicht und kann nicht entscheiden. Pruef mit:
#!/usr/bin/env bash
cat > /tmp/hook-debug.json
exit 0
Loest dein Hook aus, schau in /tmp/hook-debug.json rein. Da steht alles drin was du brauchst, tool_name, tool_input, session_id und mehr. Wenn die Datei leer ist, wird dein Hook gar nicht aufgerufen, dann liegt der Fehler weiter oben.
7. Logfile aktivieren
Sobald du diesen Schritt einmal eingerichtet hast, sparst du dir beim nächsten Mal die ersten sechs Punkte. Bau ein Mini-Log das jeder Hook am Anfang feuert:
#!/usr/bin/env bash
echo "[$(date -Iseconds)] $0 fired with TOOL=$CLAUDE_TOOL_NAME" >> ~/.claude/hooks.log
Lass eine Claude Code Session laufen, mach ein paar Aktionen, dann tail -f ~/.claude/hooks.log. Du siehst sofort welche Hooks feuern und welche nicht. 90 Prozent meiner Hook-Debugs sind so gelöst.
8. Settings-Reload triggern
Claude Code liest settings.json beim Start. Wenn du das File editierst während eine Session läuft, wird der neue Hook nicht sofort aktiv. Es gibt zwei zuverlaessige Wege: neue Session starten, oder den Settings-Reload-Befehl benutzen falls vorhanden.
Mein Workflow: ich edite die Settings, ich schließe die laufende Session mit Ctrl+C oder /exit, ich starte claude neu. Erst dann teste ich den Hook. Hatte einmal eine Stunde damit verbracht zu debuggen warum mein neuer Hook nicht feuert, bis ich realisiert habe dass die Session noch das alte Settings-File im Speicher hatte.
9. Hook-Reihenfolge bei mehreren Einträgen
Wenn du mehrere PreToolUse-Hooks definiert hast, laufen sie in der Reihenfolge in der sie im Array stehen. Sobald einer mit Exit-Code 1 endet, ist Schluss, die folgenden Hooks werden nicht mehr aufgerufen. Das ist meistens gewollt, kann dich aber verwirren wenn dein dritter Hook nie zu feuern scheint.
Pruef die Reihenfolge im settings.json, und vor allem pruef ob der erste Hook eventuell exit 0 macht wenn er nicht zustaendig ist (gut) oder ob er bei jedem Tool blockend exit 1 macht (schlecht). Standard-Pattern: am Anfang ein Filter auf Tool-Name, bei Nicht-Match sofort exit 0.
10. Nuclear Option, isoliert testen
Wenn nichts hilft, mach einen Minimaltest. Schreib ein Hook-Script das nichts tut ausser ein Log zu schreiben, haeng es als PreToolUse mit Matcher .* ein, starte eine frische Claude Code Session, gib einen Prompt der ein Tool ausloest.
{
"hooks": {
"PreToolUse": [{
"matcher": ".*",
"hooks": [{
"type": "command",
"command": "/bin/bash -c 'echo HOOK_FIRED_$(date +%s) >> /tmp/hook-test.log; exit 0'"
}]
}]
}
}
Wenn dieser Bare-Minimum-Hook nicht feuert, hast du ein Setup-Problem das nichts mit deinem eigentlichen Hook zu tun hat. Pruef Claude Code Version (claude --version), pruef ob settings.json ueberhaupt am richtigen Pfad liegt, pruef ob du vielleicht eine project-spezifische .claude/settings.json hast die die globale ueberschreibt.
Was als nächstes
Wenn der Hook jetzt feuert aber das Falsche tut, geh in Hooks gegen Halluzinationen und nutz die dort getesteten Patterns als Vorlage. Wenn du Hooks mit MCP-Tools koppeln willst, schau in MCP Tool Hooks. Und wenn du gerade erst mit Claude Code anfaengst, lohnt sich vorher Level 4 Lesson 4 (Hooks und Skills) als Grundlage.
Source
Hook-System dokumentiert auf https://code.claude.com/docs/en/hooks. Stand Mai 2026. Pattern verifiziert auf Claude Code 2.1.143.