Los hooks de Claude Code no funcionan en Windows: causas y soluciones
El mismo hook que funciona bien en un Mac no hace nada en Windows o, peor aún, bloquea todos los comandos. Casi siempre es culpa de la shell. Así es como Claude Code ejecuta los hooks en Windows y así puedes hacer que los tuyos sean fiables.
RESPUESTA CORTA
En Windows, Claude Code ejecuta el command de un hook con Git Bash, o con PowerShell si Git Bash no está instalado. Indica cuál quieres con "shell": "powershell" o "shell": "bash", limita stdout a un único objeto JSON (sin echos del perfil), usa el código de salida 2 para bloquear y actualiza Claude Code (se corrigieron varios errores de hooks en Windows en las versiones hasta la 2.1.310). Depura con /hooks y claude --debug.
Qué shell ejecuta tu hook
Claude Code pasa la cadena command a una shell: sh -c en macOS y Linux, Git Bash en Windows, o PowerShell si Git Bash no está instalado. Esa frase de la referencia de hooks explica la mayoría de los informes de “en mi Mac funciona”. Un comando escrito para PowerShell falla en Git Bash, un script de bash falla en PowerShell, y cuál te toca depende de si da la casualidad de que Git for Windows está instalado.
Elimina las conjeturas con el campo shell. Acepta "bash" o "powershell":
Si quieres Git Bash y Claude Code no lo encuentra, instala Git for Windows o establece CLAUDE_CODE_GIT_BASH_PATH con su ubicación y luego inicia una sesión nueva.
Síntomas y sus causas
| Lo que ves | Causa probable | Solución |
|---|---|---|
| El hook parece no ejecutarse nunca | Versión antigua de Claude Code, evento o matcher incorrecto, JSON no válido en la configuración | Actualiza; revisa /hooks; elimina comentarios y comas finales |
ParserError o “unexpected token” | Sintaxis de PowerShell que llega a Git Bash, o al revés | Establece shell explícitamente |
| La salida del hook “does not start with {” | Algo se imprimió antes del JSON, a menudo un perfil de shell | Silencia la salida del perfil; envía los logs a stderr |
No se encuentra una ruta que mezcla \ y / | Variable de ruta de Windows unida a un sufijo de estilo Unix | Usa la forma exec: el programa en command y cada argumento en args |
powershell: Permission denied desde Git Bash | Notificado de forma intermitente en 2.1.283 y en 2.1.304 a 2.1.305 (#103910, abierto) | Usa "shell": "powershell" para que Git Bash no esté de por medio |
| Un hook de protección lo deja pasar todo | Código de salida 1 en lugar de 2, o un error de versiones antiguas que lo dejaba pasar todo al fallar (fail-open) | Sal con 2 para bloquear; actualiza a la 2.1.310 o posterior |
| El hook vuelve al instante sin salida | Un programa del subsistema GUI al que la shell no espera | Usa un programa de consola o espera a que termine el proceso |
Solo JSON en stdout
Claude Code lee la salida estándar de tu hook como su respuesta. La regla de la documentación es breve: no imprimas en stdout nada más que el objeto JSON. En Windows esto se rompe de forma traicionera: Git Bash y algunas configuraciones siguen cargando tu perfil de shell, y un echo incondicional en ese perfil se antepone a tu JSON. Entonces el hook “funciona” en una terminal y falla dentro de Claude Code.
Escribe los logs en un archivo o en stderr, y haz que la salida de tu perfil dependa de que la shell sea interactiva.
Códigos de salida: 2 bloquea, 1 no
Salir con 0 es éxito. Salir con 2 es un error bloqueante: en los eventos que pueden bloquear, Claude Code detiene la acción y muestra tu stderr. Salir con 1 sin un JSON válido es un error no bloqueante, y la acción sigue adelante. Si tu hook aplica una política, usa el código de salida 2.
No todos los eventos se pueden bloquear así. El código de salida de un hook Notification se ignora. Un hook Stop que sale con 2 hace que Claude siga trabajando. Un hook PermissionRequest ignora por completo el código 2. Decide mediante un objeto decision en su JSON.
Claude Code 2.1.309 corrigió que los hooks de PowerShell no bloquearan cuando el programa del hook salía con código 2. Si parece que se ignora tu protección de PowerShell, comprueba primero tu versión.
Correcciones de Windows por las que vale la pena actualizar
El registro de cambios de Anthropic tiene un largo historial de correcciones de hooks en Windows. Estas son las que explican hilos antiguos de los foros:
| Versión | Corrección |
|---|---|
| 2.1.47 | Los hooks PreToolUse y PostToolUse fallaban sin avisar en Windows; ahora se ejecutan con Git Bash en lugar de cmd.exe |
| 2.1.161 | Los hooks que llamaban a bash explícitamente (/usr/bin/bash script.sh) fallaban con “command not found” |
| 2.1.299 | Los hooks PreToolUse y PermissionRequest sin shell definida lo dejaban pasar todo al fallar sin Git for Windows; ahora una protección que falla bloquea |
| 2.1.309 | Los hooks de PowerShell no bloqueaban con el código de salida 2; las protecciones con "shell": "powershell" lo dejaban pasar todo al fallar sin Git for Windows |
| 2.1.310 | Las protecciones cuyo comando consistía solo en cadenas entre comillas (como una ruta entre comillas) lo dejaban pasar todo al fallar sin Git Bash |
El issue más antiguo, titulado “No hook is working on Windows” (#10450), se notificó en la 2.0.27; un comentarista lo atribuyó a que stdin no llegaba a los comandos de los hooks. Si usas una versión tan antigua, actualiza antes de depurar nada más.
Llamar a un programa directamente
Si tu hook es un programa compilado, puedes saltarte la shell con la forma exec (el campo args; shell se ignora cuando se define args). En Windows, la forma exec exige que command se resuelva a un ejecutable real, como un .exe; los shims .cmd y .bat (incluida la mayoría de lo que instala npm) no se pueden iniciar sin una shell. Apunta al .exe o usa la forma shell.
Una lección que aprendimos al crear nuestro propio asistente de hooks: en Windows, cuando una shell inicia un programa compilado para el subsistema GUI, no espera a que termine. La shell vuelve de inmediato, el agente lee una salida vacía y el hook parece muerto. Los programas de hooks deben ser programas de consola.
Hooks de plugins y rutas
Los hooks de plugins que construyen rutas como ${CLAUDE_PLUGIN_ROOT}/hooks/run.sh dejaron de funcionar en Windows entre la v2.1.2 y la v2.1.12 (#18610): la variable se expande a una ruta con barras invertidas, y añadirle un sufijo con barra normal da una ruta mixta que bash no puede resolver. La documentación actual da la solución limpia: usa la forma exec siempre que un hook haga referencia a un marcador de ruta. Pon el programa en command y cada argumento en args, por ejemplo "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]. Cada elemento se pasa como un único argumento, sin una shell que lo destroce.
Lista de comprobación para depurar
/hooksabre una lista de solo lectura con todos los hooks configurados y su procedencia.claude --debug(o/debugen mitad de una sesión) muestra cada ejecución de hook, su salida y su código de salida.claude --debug-file <path>lo escribe en un archivo.- Valida el JSON. Los archivos de configuración son JSON estricto; una sola coma final desactiva el archivo entero.
- Revisa el matcher. Los matchers distinguen mayúsculas de minúsculas;
"*",""o la ausencia de matcher coinciden con todo. - Ejecuta el comando tú mismo en la misma shell que usará Claude Code, pasándole un JSON de ejemplo por una tubería.
- Mira el tiempo de espera. Los hooks de comando tienen un valor predeterminado de 600 segundos; un hook que espera a una persona necesita ese margen.
Para desactivar todos los hooks mientras investigas, establece "disableAllHooks": true.
Si solo querías notificaciones y aprobaciones
Muchas configuraciones de hooks en Windows existen por una sola razón: saber cuándo te necesita Claude Code y responderle. Pet Island lo hace sin scripts. Se conecta a Claude Code con un clic, se encarga por ti de los detalles de Windows y muestra cada solicitud de permiso, pregunta y plan en una pequeña isla en la parte superior de la pantalla, con el comando o el diff, donde la permites o la deniegas. También funciona cuando Claude Code se ejecuta dentro de WSL.
PREGUNTAS FRECUENTES
¿Qué shell ejecuta los hooks de Claude Code en Windows?
Git Bash, o PowerShell si Git Bash no está instalado. Establece "shell": "powershell" o "shell": "bash" en el hook para elegir explícitamente.
¿Por qué mi hook de Claude Code funciona en la terminal pero no en Claude Code?
Normalmente por salida extra: un perfil de shell que imprime texto con echo se antepone al JSON del hook. Imprime en stdout solo el objeto JSON.
¿Cómo hago que un hook bloquee una llamada a una herramienta?
Sal con el código 2 (o devuelve una decisión deny en JSON). El código de salida 1 es un error no bloqueante y la acción sigue adelante. Los hooks PermissionRequest ignoran el código 2 y usan en su lugar un objeto decision.
¿Puede el comando de un hook ser un archivo .cmd o .bat?
No en la forma exec. En Windows debe resolverse a un ejecutable real, como un .exe. Usa la forma shell para ejecutar archivos .cmd o .bat.
¿Cómo veo por qué falló un hook?
Ejecuta claude --debug o escribe /debug, y usa /hooks para ver qué hooks están cargados.
Fuentes
Cada ajuste y comportamiento de esta página se comprobó con estas páginas el 26 de octubre de 2026. Los agentes cambian rápido; si algo ya no coincide, manda la página oficial.
- Documentación de Claude Code: Hooks reference
- Documentación de Claude Code: Hooks guide
- Claude Code: CHANGELOG
- Issue #10450: Ningún hook funciona en Windows
- Issue #18610: Los hooks de plugins no pueden ejecutar scripts en Windows
- Issue #103910: Los hooks fallan de forma intermitente al iniciar powershell (Git Bash)