Les hooks de Claude Code ne fonctionnent pas sous Windows : causes et solutions
Le même hook qui fonctionne parfaitement sur Mac ne fait rien sous Windows, ou pire, bloque toutes les commandes. Presque toujours, c’est le shell. Voici comment Claude Code exécute les hooks sous Windows et comment rendre les vôtres fiables.
RÉPONSE COURTE
Sous Windows, Claude Code exécute la command d’un hook avec Git Bash, ou avec PowerShell si Git Bash n’est pas installé. Indiquez celui que vous voulez avec "shell": "powershell" ou "shell": "bash", limitez stdout à un seul objet JSON (aucun echo du profil), utilisez le code de sortie 2 pour bloquer et mettez Claude Code à jour (plusieurs bugs de hooks sous Windows ont été corrigés dans les versions jusqu’à 2.1.310). Déboguez avec /hooks et claude --debug.
Quel shell exécute votre hook
Claude Code transmet la chaîne command à un shell : sh -c sous macOS et Linux, Git Bash sous Windows, ou PowerShell si Git Bash n’est pas installé. Cette seule phrase de la référence des hooks explique la plupart des signalements du type « ça marche sur mon Mac ». Une commande écrite pour PowerShell échoue dans Git Bash, un script bash échoue dans PowerShell, et celui que vous obtenez dépend du fait que Git for Windows soit installé ou non.
Éliminez l’incertitude avec le champ shell. Il accepte "bash" ou "powershell" :
Si vous voulez Git Bash et que Claude Code ne le trouve pas, installez Git for Windows ou définissez CLAUDE_CODE_GIT_BASH_PATH sur son emplacement, puis démarrez une nouvelle session.
Les symptômes et leurs causes
| Ce que vous constatez | Cause probable | Solution |
|---|---|---|
| Le hook ne semble jamais s’exécuter | Ancienne version de Claude Code, mauvais événement ou mauvais matcher, JSON invalide dans les paramètres | Mettez à jour ; vérifiez /hooks ; supprimez les commentaires et les virgules finales |
ParserError ou « unexpected token » | De la syntaxe PowerShell qui arrive dans Git Bash, ou l’inverse | Définissez shell explicitement |
| Sortie du hook « does not start with { » | Quelque chose a été affiché avant le JSON, souvent par un profil de shell | Rendez le profil silencieux ; envoyez les journaux vers stderr |
Un chemin mêlant \ et / est introuvable | Une variable de chemin Windows concaténée avec un suffixe de style Unix | Utilisez la forme exec : le programme dans command, chaque argument dans args |
powershell: Permission denied depuis Git Bash | Signalé de façon intermittente dans 2.1.283 et 2.1.304–2.1.305 (#103910, ouverte) | Utilisez "shell": "powershell" pour que Git Bash ne soit pas intercalé |
| Un hook de garde laisse tout passer | Code de sortie 1 au lieu de 2, ou bug d’ouverture en cas d’échec (fail-open) dans les anciennes versions | Sortez avec le code 2 pour bloquer ; passez à 2.1.310 ou version ultérieure |
| Le hook rend la main immédiatement sans aucune sortie | Un programme du sous-système graphique que le shell n’attend pas | Utilisez un programme console, ou attendez la fin du processus |
Uniquement du JSON sur stdout
Claude Code lit la sortie standard de votre hook comme sa réponse. La règle de la documentation est simple : n’écrivez rien d’autre que l’objet JSON sur stdout. Sous Windows, cela casse de façon sournoise : Git Bash et certaines configurations chargent quand même votre profil de shell, et un echo inconditionnel dans ce profil se retrouve ajouté devant votre JSON. Le hook « fonctionne » alors dans un terminal et échoue dans Claude Code.
Écrivez plutôt vos journaux dans un fichier ou sur stderr, et n’affichez la sortie de votre profil que dans un shell interactif.
Codes de sortie : 2 bloque, 1 ne bloque pas
Le code de sortie 0 signifie la réussite. Le code 2 est une erreur bloquante : pour les événements qui peuvent être bloqués, Claude Code arrête l’action et affiche votre stderr. Le code 1 sans JSON valide est une erreur non bloquante, et l’action se poursuit. Si votre hook applique une stratégie, utilisez le code 2.
Tous les événements ne peuvent pas être bloqués de cette façon. Le code de sortie d’un hook Notification est ignoré. Un hook Stop qui sort avec le code 2 fait continuer Claude. Un hook PermissionRequest ignore complètement le code 2 : il décide via un objet decision dans son JSON.
Claude Code 2.1.309 a corrigé les hooks PowerShell qui ne bloquaient pas quand le programme du hook sortait avec le code 2. Si votre garde PowerShell semble ignorée, vérifiez d’abord votre version.
Les correctifs Windows qui justifient une mise à jour
Le changelog d’Anthropic contient une longue série de correctifs de hooks sous Windows. Voici ceux qui expliquent d’anciens fils de forum :
| Version | Correctif |
|---|---|
| 2.1.47 | Les hooks PreToolUse et PostToolUse échouaient silencieusement sous Windows ; ils s’exécutent désormais avec Git Bash au lieu de cmd.exe |
| 2.1.161 | Les hooks qui appellent bash explicitement (/usr/bin/bash script.sh) échouaient avec « command not found » |
| 2.1.299 | Les hooks PreToolUse et PermissionRequest sans shell défini laissaient tout passer en cas d’échec sans Git for Windows ; une garde en échec bloque désormais |
| 2.1.309 | Les hooks PowerShell ne bloquaient pas avec le code de sortie 2 ; les gardes "shell": "powershell" laissaient tout passer sans Git for Windows |
| 2.1.310 | Les gardes dont la commande n’était composée que de chaînes entre guillemets (comme un chemin entre guillemets) laissaient tout passer sans Git Bash |
L’ancienne issue intitulée « No hook is working on Windows » (#10450) a été signalée sur la 2.0.27 ; un commentateur l’a attribuée à stdin qui n’atteignait pas les commandes des hooks. Si vous utilisez une version aussi ancienne, mettez à jour avant de déboguer quoi que ce soit d’autre.
Appeler un programme directement
Si votre hook est un programme compilé, vous pouvez vous passer du shell avec la forme exec (le champ args ; shell est ignoré quand args est défini). Sous Windows, la forme exec exige que command corresponde à un véritable exécutable, comme un .exe ; les shims .cmd et .bat (dont la plupart des éléments installés par npm) ne peuvent pas être lancés sans shell. Pointez vers le .exe, ou utilisez la forme shell.
Une leçon tirée du développement de notre propre utilitaire de hook : sous Windows, un programme compilé pour le sous-système graphique n’est pas attendu quand un shell le lance. Le shell rend la main aussitôt, l’agent lit une sortie vide, et le hook semble mort. Les programmes de hook doivent être des programmes console.
Hooks de plugins et chemins
Les hooks de plugins qui construisent des chemins comme ${CLAUDE_PLUGIN_ROOT}/hooks/run.sh ne fonctionnaient plus sous Windows entre la v2.1.2 et la v2.1.12 (#18610) : la variable se développe en un chemin avec des barres obliques inverses, et l’ajout d’un suffixe avec des barres obliques donne un chemin mixte que bash ne sait pas résoudre. La documentation actuelle donne la solution propre : utilisez la forme exec chaque fois qu’un hook fait référence à un espace réservé de chemin. Placez le programme dans command et chaque argument dans args, par exemple "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]. Chaque élément est transmis comme un seul argument, sans shell pour le déformer.
Liste de vérification pour le débogage
/hooksouvre une liste en lecture seule de tous les hooks configurés et de leur provenance.claude --debug(ou/debugen cours de session) affiche chaque exécution de hook, sa sortie et son code de sortie.claude --debug-file <path>l’écrit dans un fichier.- Validez le JSON. Les fichiers de paramètres sont du JSON strict ; une seule virgule finale désactive tout le fichier.
- Vérifiez le matcher. Les matchers sont sensibles à la casse ;
"*",""ou l’absence de matcher correspondent à tout. - Exécutez la commande vous-même dans le shell que Claude Code utilisera, en lui envoyant un exemple de JSON par un pipe.
- Vérifiez le délai d’expiration. Les hooks de commande ont un délai par défaut de 600 secondes ; un hook qui attend une personne a besoin de cette marge.
Pour désactiver tous les hooks pendant votre investigation, définissez "disableAllHooks": true.
Si vous vouliez seulement des notifications et des approbations
Beaucoup de configurations de hooks sous Windows n’existent que pour une raison : savoir quand Claude Code a besoin de vous et lui répondre. Pet Island le fait sans aucun script. Il se connecte à Claude Code en un clic, gère les détails propres à Windows à votre place et affiche chaque demande d’autorisation, chaque question et chaque plan sur une petite île en haut de l’écran, avec la commande ou le diff, où vous les autorisez ou les refusez. Il fonctionne aussi quand Claude Code s’exécute dans WSL.
FAQ
Quel shell exécute les hooks de Claude Code sous Windows ?
Git Bash, ou PowerShell si Git Bash n’est pas installé. Définissez "shell": "powershell" ou "shell": "bash" dans le hook pour choisir explicitement.
Pourquoi mon hook Claude Code fonctionne-t-il dans le terminal mais pas dans Claude Code ?
En général à cause d’une sortie en trop : un profil de shell qui affiche du texte se retrouve devant le JSON du hook. N’écrivez que l’objet JSON sur stdout.
Comment faire en sorte qu’un hook bloque un appel d’outil ?
Sortez avec le code 2 (ou renvoyez une décision deny en JSON). Le code de sortie 1 est une erreur non bloquante et l’action se poursuit. Les hooks PermissionRequest ignorent le code 2 et utilisent un objet decision à la place.
La commande d’un hook peut-elle être un fichier .cmd ou .bat ?
Pas en forme exec. Sous Windows, elle doit correspondre à un véritable exécutable, comme un .exe. Utilisez la forme shell pour exécuter des fichiers .cmd ou .bat.
Comment savoir pourquoi un hook a échoué ?
Lancez claude --debug ou tapez /debug, et utilisez /hooks pour voir quels hooks sont chargés.
Sources
Chaque paramètre et chaque comportement décrits sur cette page ont été vérifiés dans ces pages le 26 octobre 2026. Les agents évoluent vite : si quelque chose ne correspond plus, c’est la page officielle qui fait foi.
- Documentation Claude Code : Hooks reference
- Documentation Claude Code : Hooks guide
- Claude Code : CHANGELOG
- Issue #10450 : aucun hook ne fonctionne sous Windows
- Issue #18610 : les hooks de plugins ne peuvent pas exécuter de scripts sous Windows
- Issue #103910 : échec intermittent du lancement de powershell par les hooks (Git Bash)