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.

Por Aslan, equipo de Pet Island · Actualizado el 26 oct 2026 · 5 min de lectura

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":

%USERPROFILE%\.claude\settings.jsonCopiar
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "[Console]::Beep(880, 200)"
}
]
}
]
}
}

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 vesCausa probableSolución
El hook parece no ejecutarse nuncaVersión antigua de Claude Code, evento o matcher incorrecto, JSON no válido en la configuraciónActualiza; revisa /hooks; elimina comentarios y comas finales
ParserError o “unexpected token”Sintaxis de PowerShell que llega a Git Bash, o al revésEstablece shell explícitamente
La salida del hook “does not start with {”Algo se imprimió antes del JSON, a menudo un perfil de shellSilencia 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 UnixUsa la forma exec: el programa en command y cada argumento en args
powershell: Permission denied desde Git BashNotificado 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 todoCó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 salidaUn programa del subsistema GUI al que la shell no esperaUsa 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ónCorrección
2.1.47Los hooks PreToolUse y PostToolUse fallaban sin avisar en Windows; ahora se ejecutan con Git Bash en lugar de cmd.exe
2.1.161Los hooks que llamaban a bash explícitamente (/usr/bin/bash script.sh) fallaban con “command not found”
2.1.299Los 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.309Los 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.310Las 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

  1. /hooks abre una lista de solo lectura con todos los hooks configurados y su procedencia.
  2. claude --debug (o /debug en 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.
  3. Valida el JSON. Los archivos de configuración son JSON estricto; una sola coma final desactiva el archivo entero.
  4. Revisa el matcher. Los matchers distinguen mayúsculas de minúsculas; "*", "" o la ausencia de matcher coinciden con todo.
  5. Ejecuta el comando tú mismo en la misma shell que usará Claude Code, pasándole un JSON de ejemplo por una tubería.
  6. 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.

SIGUE LEYENDO

Responde a todos tus agentes desde la parte superior de la pantalla.

Windows y Mac · 7 días gratis, después un único pago de $8.99 · $1 de cada venta alimenta a animales callejeros

Prueba Pet Island gratis