Gli hook di Claude Code non funzionano su Windows: cause e soluzioni
Lo stesso hook che funziona benissimo su Mac non fa nulla su Windows o, peggio, blocca ogni comando. Quasi sempre la colpa è della shell. Ecco come Claude Code esegue gli hook su Windows e come rendere affidabili i tuoi.
RISPOSTA BREVE
Su Windows, Claude Code esegue il command di un hook con Git Bash, oppure con PowerShell se Git Bash non è installato. Indica quale intendi con "shell": "powershell" o "shell": "bash", limita stdout a un solo oggetto JSON (niente echo dal profilo), usa il codice di uscita 2 per bloccare e aggiorna Claude Code (diversi bug degli hook su Windows sono stati corretti nelle versioni fino alla 2.1.310). Esegui il debug con /hooks e claude --debug.
Quale shell esegue il tuo hook
Claude Code passa la stringa command a una shell: sh -c su macOS e Linux, Git Bash su Windows, oppure PowerShell se Git Bash non è installato. Questa sola frase del riferimento degli hook spiega la maggior parte delle segnalazioni del tipo “sul mio Mac funziona”. Un comando scritto per PowerShell fallisce in Git Bash, uno script bash fallisce in PowerShell, e quale ti tocca dipende dal fatto che Git for Windows sia installato o meno.
Elimina l'incertezza con il campo shell. Accetta "bash" o "powershell":
Se vuoi Git Bash e Claude Code non lo trova, installa Git for Windows o imposta CLAUDE_CODE_GIT_BASH_PATH sul suo percorso, poi avvia una nuova sessione.
I sintomi e le loro cause
| Cosa vedi | Causa probabile | Soluzione |
|---|---|---|
| L'hook sembra non essere mai eseguito | Versione vecchia di Claude Code, evento o matcher sbagliato, JSON non valido nelle impostazioni | Aggiorna; controlla /hooks; rimuovi commenti e virgole finali |
ParserError o “unexpected token” | Sintassi PowerShell che arriva a Git Bash, o viceversa | Imposta shell in modo esplicito |
| Output dell'hook “does not start with {” | Qualcosa è stato stampato prima del JSON, spesso dal profilo della shell | Silenzia l'output del profilo; invia i log a stderr |
Un percorso con \ e / mescolati non viene trovato | Variabile di percorso Windows unita a un suffisso in stile Unix | Usa la forma exec: il programma in command, ogni argomento in args |
powershell: Permission denied da Git Bash | Segnalato in modo intermittente nella 2.1.283 e nelle 2.1.304–2.1.305 (#103910, aperta) | Usa "shell": "powershell" così Git Bash non si mette in mezzo |
| Un hook di controllo lascia passare tutto | Codice di uscita 1 invece di 2, oppure un bug fail-open nelle versioni precedenti | Esci con 2 per bloccare; aggiorna alla 2.1.310 o successiva |
| L'hook termina subito senza output | Un programma del sottosistema GUI che la shell non attende | Usa un programma console, oppure attendi il processo |
Solo JSON su stdout
Claude Code legge l'output standard dell'hook come sua risposta. La regola nella documentazione è breve: non stampare su stdout nient'altro che l'oggetto JSON. Su Windows questo si rompe in modo subdolo: Git Bash e alcune configurazioni caricano comunque il profilo della shell, e un echo incondizionato in quel profilo finisce davanti al tuo JSON. L'hook allora “funziona” in un terminale e fallisce dentro Claude Code.
Scrivi invece i log in un file o su stderr, e fai in modo che il profilo produca output solo in una shell interattiva.
Codici di uscita: 2 blocca, 1 no
L'uscita 0 indica successo. L'uscita 2 è un errore bloccante: negli eventi che possono essere bloccati, Claude Code ferma l'azione e mostra il tuo stderr. L'uscita 1 senza JSON valido è un errore non bloccante, e l'azione prosegue. Se il tuo hook applica un criterio, usa l'uscita 2.
Non tutti gli eventi possono essere bloccati in questo modo. Il codice di uscita di un hook Notification viene ignorato. Un hook Stop che esce con 2 fa continuare Claude a lavorare. Un hook PermissionRequest ignora del tutto l'uscita 2: decide tramite un oggetto decision nel suo JSON.
Claude Code 2.1.309 ha corretto gli hook PowerShell che non bloccavano quando il programma dell'hook usciva con codice 2. Se il tuo controllo in PowerShell sembra ignorato, verifica prima la versione.
Le correzioni per Windows per cui vale la pena aggiornare
Il changelog di Anthropic contiene una lunga serie di correzioni degli hook su Windows. Quelle che spiegano vecchie discussioni nei forum:
| Versione | Correzione |
|---|---|
| 2.1.47 | Gli hook PreToolUse e PostToolUse fallivano in silenzio su Windows; ora vengono eseguiti con Git Bash invece di cmd.exe |
| 2.1.161 | Gli hook che chiamano bash in modo esplicito (/usr/bin/bash script.sh) fallivano con “command not found” |
| 2.1.299 | Gli hook PreToolUse e PermissionRequest senza shell impostata lasciavano passare tutto in caso di errore senza Git for Windows; ora un controllo che fallisce blocca |
| 2.1.309 | Gli hook PowerShell non bloccavano con il codice di uscita 2; i controlli con "shell": "powershell" lasciavano passare tutto senza Git for Windows |
| 2.1.310 | I controlli il cui comando era composto solo da stringhe tra virgolette (come un percorso tra virgolette) lasciavano passare tutto senza Git Bash |
La vecchia issue intitolata “No hook is working on Windows” (#10450) è stata segnalata sulla 2.0.27; un utente nei commenti ha ricondotto il problema allo stdin che non arrivava ai comandi degli hook. Se usi una versione così vecchia, aggiorna prima di fare qualsiasi altro debug.
Chiamare direttamente un programma
Se il tuo hook è un programma compilato, puoi saltare la shell con la forma exec (il campo args; shell viene ignorato quando args è impostato). Su Windows, la forma exec richiede che command corrisponda a un vero eseguibile come un .exe; gli shim .cmd e .bat (compresa la maggior parte di ciò che installa npm) non possono essere avviati senza una shell. Punta al .exe, oppure usa la forma shell.
Una lezione imparata sviluppando il nostro helper per gli hook: su Windows, un programma compilato per il sottosistema GUI non viene atteso quando una shell lo avvia. La shell restituisce subito il controllo, l'agente legge un output vuoto e l'hook sembra morto. I programmi per gli hook devono essere programmi console.
Hook dei plugin e percorsi
Gli hook dei plugin che costruiscono percorsi come ${CLAUDE_PLUGIN_ROOT}/hooks/run.sh non funzionavano su Windows tra la v2.1.2 e la v2.1.12 (#18610): la variabile si espande in un percorso con barre rovesciate, e aggiungere un suffisso con barre normali produce un percorso misto che bash non sa risolvere. La documentazione attuale indica la soluzione pulita: usa la forma exec ogni volta che un hook fa riferimento a un segnaposto di percorso. Metti il programma in command e ogni argomento in args, ad esempio "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]. Ogni elemento viene passato come un singolo argomento, senza una shell che lo stravolga.
Checklist per il debug
/hooksapre un elenco di sola lettura di tutti gli hook configurati e della loro provenienza.claude --debug(o/debuga sessione in corso) mostra ogni esecuzione di hook, il suo output e il codice di uscita.claude --debug-file <path>lo scrive in un file.- Convalida il JSON. I file di impostazioni sono JSON rigoroso; una sola virgola finale disattiva l'intero file.
- Controlla il matcher. I matcher distinguono tra maiuscole e minuscole;
"*",""o nessun matcher corrispondono a tutto. - Esegui tu il comando nella stessa shell che userà Claude Code, passandogli in pipe un JSON di esempio.
- Guarda il timeout. Gli hook di comando hanno un timeout predefinito di 600 secondi; un hook che attende una persona ha bisogno di quel margine.
Per disattivare tutti gli hook mentre indaghi, imposta "disableAllHooks": true.
Se volevi solo notifiche e approvazioni
Molte configurazioni di hook su Windows esistono per un solo motivo: sapere quando Claude Code ha bisogno di te e rispondergli. Pet Island lo fa senza alcuno script. Si collega a Claude Code con un clic, gestisce per te i dettagli di Windows e mette ogni richiesta di autorizzazione, domanda e piano su una piccola isola nella parte alta dello schermo, con il comando o il diff, dove puoi consentirli o negarli. Funziona anche quando Claude Code viene eseguito in WSL.
FAQ
Quale shell esegue gli hook di Claude Code su Windows?
Git Bash, oppure PowerShell se Git Bash non è installato. Imposta "shell": "powershell" o "shell": "bash" nell'hook per scegliere in modo esplicito.
Perché il mio hook di Claude Code funziona nel terminale ma non in Claude Code?
Di solito per un output in più: un profilo della shell che stampa del testo finisce davanti al JSON dell'hook. Stampa su stdout solo l'oggetto JSON.
Come faccio a far bloccare una chiamata a uno strumento da un hook?
Esci con codice 2 (oppure restituisci una decisione deny in JSON). Il codice di uscita 1 è un errore non bloccante e l'azione prosegue. Gli hook PermissionRequest ignorano l'uscita 2 e usano invece un oggetto decision.
Il comando di un hook può essere un file .cmd o .bat?
Non nella forma exec. Su Windows deve corrispondere a un vero eseguibile come un .exe. Usa la forma shell per eseguire file .cmd o .bat.
Come vedo perché un hook è fallito?
Esegui claude --debug o digita /debug, e usa /hooks per vedere quali hook sono caricati.
Fonti
Ogni impostazione e comportamento descritti in questa pagina sono stati verificati su queste pagine il 26 ottobre 2026. Gli agenti cambiano in fretta: se qualcosa non corrisponde più, fa fede la pagina ufficiale.
- Documentazione di Claude Code: Hooks reference
- Documentazione di Claude Code: Hooks guide
- Claude Code: CHANGELOG
- Issue #10450: nessun hook funziona su Windows
- Issue #18610: gli hook dei plugin non riescono a eseguire script su Windows
- Issue #103910: gli hook a volte non riescono ad avviare powershell (Git Bash)