Claude-Code-Hooks funktionieren unter Windows nicht: Ursachen und Lösungen
Derselbe Hook, der auf dem Mac problemlos läuft, tut unter Windows nichts oder blockiert, schlimmer noch, jeden Befehl. Fast immer liegt es an der Shell. So führt Claude Code Hooks unter Windows aus, und so machst du deine zuverlässig.
KURZE ANTWORT
Unter Windows führt Claude Code das command eines Hooks mit Git Bash aus oder mit PowerShell, wenn Git Bash nicht installiert ist. Leg mit "shell": "powershell" oder "shell": "bash" fest, welche du meinst, halte stdout auf ein einziges JSON-Objekt beschränkt (keine Profil-Echos), blockiere mit Exit-Code 2 und aktualisiere Claude Code (bis Version 2.1.310 wurden mehrere Windows-Hook-Fehler behoben). Debugge mit /hooks und claude --debug.
Welche Shell deinen Hook ausführt
Claude Code übergibt den command-String an eine Shell: sh -c unter macOS und Linux, Git Bash unter Windows oder PowerShell, wenn Git Bash nicht installiert ist. Dieser eine Satz aus der Hooks-Referenz erklärt die meisten „auf meinem Mac geht’s“-Meldungen. Ein für PowerShell geschriebener Befehl scheitert in Git Bash, ein Bash-Skript scheitert in PowerShell, und welche Shell du bekommst, hängt davon ab, ob Git for Windows zufällig installiert ist.
Mit dem Feld shell musst du nicht mehr raten. Es akzeptiert "bash" oder "powershell":
Wenn du Git Bash willst und Claude Code es nicht findet, installiere Git for Windows oder setz CLAUDE_CODE_GIT_BASH_PATH auf den Speicherort und starte dann eine neue Sitzung.
Symptome und ihre Ursachen
| Was du siehst | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| Der Hook scheint nie zu laufen | Alte Claude-Code-Version, falsches Event oder falscher Matcher, ungültiges JSON in den Einstellungen | Aktualisieren; /hooks prüfen; Kommentare und abschließende Kommas entfernen |
ParserError oder „unexpected token“ | PowerShell-Syntax landet in Git Bash oder umgekehrt | shell explizit setzen |
| Hook-Ausgabe „does not start with {“ | Vor dem JSON wurde etwas ausgegeben, oft von einem Shell-Profil | Profilausgaben unterdrücken; Logs nach stderr schicken |
Ein Pfad mit gemischten \ und / wird nicht gefunden | Windows-Pfadvariable mit einem Suffix im Unix-Stil verbunden | Exec-Form verwenden: Programm in command, jedes Argument in args |
powershell: Permission denied aus Git Bash | Sporadisch gemeldet in 2.1.283 und 2.1.304–2.1.305 (#103910, offen) | "shell": "powershell" verwenden, damit Git Bash nicht dazwischen hängt |
| Ein Guard-Hook lässt alles durch | Exit-Code 1 statt 2 oder ein Fail-open-Fehler in älteren Versionen | Mit 2 beenden, um zu blockieren; auf 2.1.310 oder neuer aktualisieren |
| Der Hook kehrt sofort ohne Ausgabe zurück | Ein Programm für das GUI-Subsystem, auf das die Shell nicht wartet | Ein Konsolenprogramm verwenden oder auf den Prozess warten |
Nur JSON auf stdout
Claude Code liest die Standardausgabe deines Hooks als dessen Antwort. Die Regel in der Doku ist kurz: Gib auf stdout nichts außer dem JSON-Objekt aus. Unter Windows geht das auf hinterhältige Weise kaputt: Git Bash und manche Setups laden trotzdem dein Shell-Profil, und ein bedingungsloses echo darin landet vor deinem JSON. Der Hook „funktioniert“ dann im Terminal und scheitert in Claude Code.
Logge stattdessen in eine Datei oder nach stderr, und mach die Ausgaben deines Profils davon abhängig, dass die Shell interaktiv ist.
Exit-Codes: 2 blockiert, 1 nicht
Exit 0 bedeutet Erfolg. Exit 2 ist ein blockierender Fehler: Bei Events, die blockiert werden können, stoppt Claude Code die Aktion und zeigt deine stderr-Ausgabe. Exit 1 ohne gültiges JSON ist ein nicht blockierender Fehler, und die Aktion läuft weiter. Wenn dein Hook eine Richtlinie durchsetzt, verwende Exit 2.
Nicht jedes Event lässt sich so blockieren. Der Exit-Code eines Notification-Hooks wird ignoriert. Ein Stop-Hook, der mit 2 endet, lässt Claude weiterarbeiten. Ein PermissionRequest-Hook ignoriert Exit 2 komplett; er entscheidet über ein decision-Objekt in seinem JSON.
Claude Code 2.1.309 hat behoben, dass PowerShell-Hooks nicht blockierten, wenn das Programm des Hooks mit Code 2 endete. Wenn dein PowerShell-Guard ignoriert zu werden scheint, prüf zuerst deine Version.
Windows-Fixes, für die sich ein Update lohnt
Anthropics Changelog enthält eine lange Reihe von Windows-Hook-Fixes. Diese erklären alte Forenthreads:
| Version | Fix |
|---|---|
| 2.1.47 | PreToolUse- und PostToolUse-Hooks scheiterten unter Windows stillschweigend; sie laufen jetzt mit Git Bash statt cmd.exe |
| 2.1.161 | Hooks, die bash explizit aufrufen (/usr/bin/bash script.sh), scheiterten mit „command not found“ |
| 2.1.299 | PreToolUse- und PermissionRequest-Hooks ohne gesetzte Shell waren ohne Git for Windows fail-open; ein fehlschlagender Guard blockiert jetzt |
| 2.1.309 | PowerShell-Hooks blockierten bei Exit-Code 2 nicht; "shell": "powershell"-Guards waren ohne Git for Windows fail-open |
| 2.1.310 | Guards, deren Befehl nur aus Strings in Anführungszeichen bestand (etwa ein Pfad in Anführungszeichen), waren ohne Git Bash fail-open |
Das ältere Issue mit dem Titel „No hook is working on Windows“ (#10450) wurde unter 2.0.27 gemeldet; ein Kommentator führte es darauf zurück, dass stdin die Hook-Befehle nicht erreichte. Wenn du eine so alte Version nutzt, aktualisiere, bevor du irgendetwas anderes debuggst.
Ein Programm direkt aufrufen
Wenn dein Hook ein kompiliertes Programm ist, kannst du die Shell mit der Exec-Form umgehen (Feld args; shell wird ignoriert, wenn args gesetzt ist). Unter Windows muss command in der Exec-Form auf eine echte ausführbare Datei wie eine .exe auflösen; .cmd- und .bat-Shims (darunter das meiste, was npm installiert) lassen sich ohne Shell nicht starten. Verweise auf die .exe oder nutze die Shell-Form.
Eine Lektion aus der Entwicklung unseres eigenen Hook-Helfers: Unter Windows wird auf ein Programm für das GUI-Subsystem nicht gewartet, wenn eine Shell es startet. Die Shell kehrt sofort zurück, der Agent liest eine leere Ausgabe, und der Hook wirkt tot. Hook-Programme müssen Konsolenprogramme sein.
Plugin-Hooks und Pfade
Plugin-Hooks, die Pfade wie ${CLAUDE_PLUGIN_ROOT}/hooks/run.sh zusammensetzen, waren unter Windows zwischen v2.1.2 und v2.1.12 kaputt (#18610): Die Variable wird zu einem Pfad mit Backslashes, und ein angehängtes Suffix mit Schrägstrichen ergibt einen gemischten Pfad, den bash nicht auflösen kann. Die aktuelle Doku nennt die saubere Lösung: Verwende die Exec-Form, wann immer ein Hook auf einen Pfad-Platzhalter verweist. Leg das Programm in command und jedes Argument in args, zum Beispiel "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]. Jedes Element wird als ein Argument übergeben, ohne Shell, die es verstümmeln könnte.
Checkliste fürs Debugging
/hooksöffnet eine schreibgeschützte Liste aller konfigurierten Hooks und ihrer Herkunft.claude --debug(oder/debugmitten in der Sitzung) zeigt jeden Hook-Lauf, seine Ausgabe und seinen Exit-Code.claude --debug-file <path>schreibt das in eine Datei.- Validiere das JSON. Einstellungsdateien sind striktes JSON; ein einziges abschließendes Komma deaktiviert die ganze Datei.
- Prüf den Matcher. Matcher unterscheiden Groß- und Kleinschreibung;
"*",""oder kein Matcher erfasst alles. - Führ den Befehl selbst aus, in derselben Shell, die Claude Code verwenden wird, mit per Pipe übergebenem Beispiel-JSON.
- Achte auf das Timeout. Befehls-Hooks haben standardmäßig 600 Sekunden; ein Hook, der auf einen Menschen wartet, braucht diesen Spielraum.
Um während der Untersuchung alle Hooks abzuschalten, setz "disableAllHooks": true.
Wenn du nur Benachrichtigungen und Freigaben wolltest
Viele Windows-Hook-Setups gibt es nur aus einem Grund: um zu wissen, wann Claude Code dich braucht, und darauf zu antworten. Pet Island erledigt das ganz ohne Skripte. Es verbindet sich mit einem Klick mit Claude Code, kümmert sich um die Windows-Details und zeigt jede Berechtigungsanfrage, jede Frage und jeden Plan auf einer kleinen Insel oben auf dem Bildschirm, mit dem Befehl oder dem Diff, wo du sie erlaubst oder ablehnst. Es funktioniert auch, wenn Claude Code in WSL läuft.
FAQ
Welche Shell führt Claude-Code-Hooks unter Windows aus?
Git Bash oder PowerShell, wenn Git Bash nicht installiert ist. Setz im Hook "shell": "powershell" oder "shell": "bash", um explizit zu wählen.
Warum funktioniert mein Claude-Code-Hook im Terminal, aber nicht in Claude Code?
Meist wegen zusätzlicher Ausgaben: Ein Shell-Profil, das Text ausgibt, landet vor dem JSON des Hooks. Gib auf stdout nur das JSON-Objekt aus.
Wie bringe ich einen Hook dazu, einen Tool-Aufruf zu blockieren?
Beende ihn mit Code 2 (oder gib im JSON eine Deny-Entscheidung zurück). Exit-Code 1 ist ein nicht blockierender Fehler, und die Aktion läuft weiter. PermissionRequest-Hooks ignorieren Exit 2 und verwenden stattdessen ein decision-Objekt.
Kann ein Hook-Befehl eine .cmd- oder .bat-Datei sein?
Nicht in der Exec-Form. Unter Windows muss er auf eine echte ausführbare Datei wie eine .exe auflösen. Nutze die Shell-Form, um .cmd- oder .bat-Dateien auszuführen.
Wie sehe ich, warum ein Hook fehlgeschlagen ist?
Führ claude --debug aus oder tippe /debug, und nutze /hooks, um zu sehen, welche Hooks geladen sind.
Quellen
Jede Einstellung und jedes Verhalten auf dieser Seite wurde am 26. Oktober 2026 mit diesen Seiten abgeglichen. Agenten ändern sich schnell; wenn etwas nicht mehr passt, gilt die offizielle Seite.
- Claude-Code-Doku: Hooks reference
- Claude-Code-Doku: Hooks guide
- Claude Code: CHANGELOG
- Issue #10450: Unter Windows funktioniert kein Hook
- Issue #18610: Plugin-Hooks können unter Windows keine Skripte ausführen
- Issue #103910: Hooks können powershell sporadisch nicht starten (Git Bash)