← Alle Playbooks
Playbook· setup

Depurar hooks cuando no se dispara nada

Diez pasos para averiguar por qué tu hook de Claude Code no se activa. Desde el error de tecleo en el JSON hasta el patrón del matcher.

Has configurado el hook. Editas un archivo, lanzas Bash, escribes un prompt. Y no pasa nada. Ni bloqueo, ni log, ni una señal de que tu hook exista siquiera. Este es el fallo más habitual con los hooks, y casi siempre se debe a una de diez cosas. Las repaso por orden.

El requisito es que ya hayas pasado una vez por el playbook de hooks (Hooks contra alucinaciones) y tengas un hook puesto en ~/.claude/settings.json. Si no es así, empieza ahí y vuelve aquí cuando no se dispare.

1. Comprueba que el archivo de settings sea JSON válido

Primer punto y, por desgracia, también el más frecuente. Una coma que falta, una llave de cierre de más, unas comillas tipográficas en vez de las normales. Claude Code no lo registra a voces, simplemente ignora la sección de hooks en silencio.

cat ~/.claude/settings.json | jq .

Si jq lanza un error de parseo, ahí tienes tu fallo. Si jq pasa limpio, sigue adelante. En macOS y Linux settings.json está siempre en ~/.claude/, en Windows en la ruta de AppData Roaming.

2. La ruta del hook tiene que ser absoluta

Las rutas relativas en la propiedad command no funcionan de forma fiable, porque Claude Code se arranca desde directorios de trabajo muy distintos. Pon la ruta completa, con $HOME ya resuelto:

{
  "type": "command",
  "command": "/home/du/.claude/hooks/read-before-edit.sh"
}

Ni ./hooks/read-before-edit.sh ni ~/.claude/hooks/read-before-edit.sh sin expansión de shell. La tilde no se expande dentro de un JSON.

3. El script del hook tiene que ser ejecutable

Este se me olvida a mí mismo con regularidad. El script existe, la ruta es correcta, pero no es ejecutable y Claude Code lanza en segundo plano un permission denied que no ves por ninguna parte.

chmod +x ~/.claude/hooks/*.sh
ls -la ~/.claude/hooks/

Los flags x tienen que estar. Si acabas de crear tu script, muchas veces faltan.

4. ¿El matcher coincide de verdad con la herramienta?

Los hooks tienen un matcher que va por regex contra el nombre de la herramienta. Edit|Write|MultiEdit es un conjunto de herramientas distinto de mcp__filesystem__write_file. Si tu hook apunta a ediciones de archivos pero tu modelo está escribiendo por MCP, el hook no se dispara. A mayo de 2026 las herramientas nativas son Read, Write, Edit, MultiEdit, Bash, Glob, Grep. Todo lo que lleva el prefijo mcp__ es MCP y necesita su propio matcher.

Puedes probarlo con la herramienta de regex que prefieras, o simplemente con un hook de bypass que haga match con .* y solo registre qué herramientas aparecen durante la ejecución. Eso lleva directo al punto 7.

5. En PreToolUse lo que cuenta es el código de salida

Los hooks PreToolUse bloquean la ejecución de la herramienta cuando el código de salida es distinto de 0. Si tu script revienta por dentro pero acaba con exit 0 (por ejemplo por un set +e al principio, o porque la última instrucción salió bien), Claude Code da por hecho que todo está en orden y ejecuta la herramienta.

Un patrón que funciona: set -e al principio, luego comprobaciones dirigidas con un exit 1 explícito y un mensaje por stderr. Ejemplo:

#!/usr/bin/env bash
set -e

INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ -z "$FILE" ]]; then
  exit 0  # nicht unser Tool
fi

if ! grep -q "$FILE" "$CLAUDE_SESSION_LOG"; then
  echo "Blocked: $FILE wurde noch nicht gelesen" >&2
  exit 1
fi

Stderr se ve en la consola de Claude Code, stdout se va al pipeline de output de la herramienta. Si confundes los dos, Claude Code toma tu mensaje de bloqueo por un resultado de la herramienta.

6. Stdin lleva el input de la herramienta como JSON

Claude Code entrega la llamada completa a la herramienta como JSON por stdin. Si tu script no hace un cat o un read, no ve ese input y no puede decidir nada. Compruébalo con:

#!/usr/bin/env bash
cat > /tmp/hook-debug.json
exit 0

Dispara tu hook y luego mira dentro de /tmp/hook-debug.json. Ahí está todo lo que necesitas, tool_name, tool_input, session_id y más. Si el archivo está vacío, tu hook no se está llamando en absoluto, y entonces el fallo está más arriba.

7. Activa un logfile

En cuanto tengas montado este paso, la próxima vez te ahorras los seis primeros puntos. Monta un mini log que dispare cada hook al principio:

#!/usr/bin/env bash
echo "[$(date -Iseconds)] $0 fired with TOOL=$CLAUDE_TOOL_NAME" >> ~/.claude/hooks.log

Deja correr una sesión de Claude Code, haz un par de acciones y luego tail -f ~/.claude/hooks.log. Ves al instante qué hooks se disparan y cuáles no. El 90 por ciento de mis depuraciones de hooks se resuelven así.

8. Fuerza una recarga de los settings

Claude Code lee settings.json al arrancar. Si editas el archivo mientras hay una sesión en marcha, el hook nuevo no se activa de inmediato. Hay dos caminos fiables: arrancar una sesión nueva, o usar el comando de recarga de settings si existe.

Mi workflow: edito los settings, cierro la sesión en marcha con Ctrl+C o /exit, arranco claude de nuevo. Solo entonces pruebo el hook. Una vez me pasé una hora depurando por qué mi hook nuevo no se disparaba, hasta que caí en que la sesión seguía teniendo en memoria el archivo de settings viejo.

9. Orden de los hooks cuando hay varias entradas

Si has definido varios hooks PreToolUse, corren en el orden en el que están en el array. En cuanto uno termina con código de salida 1, se acabó, los hooks siguientes ya no se llaman. Eso suele ser lo que quieres, pero puede despistarte cuando tu tercer hook parece no dispararse nunca.

Comprueba el orden en el settings.json y, sobre todo, comprueba si el primer hook hace exit 0 cuando no le toca (bien) o si bloquea con exit 1 en cada herramienta (mal). Patrón estándar: al principio un filtro por nombre de herramienta y, si no hay match, exit 0 inmediato.

10. Opción nuclear, probar en aislado

Si nada ayuda, haz una prueba mínima. Escribe un script de hook que no haga nada más que escribir un log, engánchalo como PreToolUse con el matcher .*, arranca una sesión limpia de Claude Code y lanza un prompt que dispare alguna herramienta.

{
  "hooks": {
    "PreToolUse": [{
      "matcher": ".*",
      "hooks": [{
        "type": "command",
        "command": "/bin/bash -c 'echo HOOK_FIRED_$(date +%s) >> /tmp/hook-test.log; exit 0'"
      }]
    }]
  }
}

Si este hook de mínimos no se dispara, tienes un problema de setup que no tiene nada que ver con tu hook de verdad. Comprueba la versión de Claude Code (claude --version), comprueba si settings.json está siquiera en la ruta correcta, comprueba si a lo mejor tienes un .claude/settings.json específico del proyecto que sobrescribe al global.

Qué viene después

Si el hook ya se dispara pero hace lo que no debe, métete en Hooks contra alucinaciones y usa como plantilla los patrones probados que hay allí. Si quieres acoplar hooks con herramientas MCP, mira Hooks para herramientas MCP. Y si acabas de empezar con Claude Code, antes te compensa el Nivel 4, lección 4 (Hooks y skills) como base.

Source

Sistema de hooks documentado en https://code.claude.com/docs/en/hooks. Estado de mayo de 2026. Patrones verificados en Claude Code 2.1.143.