Blog • Claude Code / Agent SDK
Après la mise à niveau vers Claude Code 2.1.317 avec onFailure block sur les hooks Agent SDK, comment consigner les blocages quand un callback hook échoue dans les logs JSON ?
Le 3 novembre 2026, Anthropic a livré Claude Code 2.1.317. Les callbacks hook Agent SDK ajoutent onFailure block : si un script PreToolUse sort non zéro ou time out, le client ne fail-open plus vers le tool mais bloque l action et logue blocked dans l audit JSON.
Le JSON résultat callback hook porte hookName, onFailure block, exitCode, timedOut ; l audit post-blocage : decision blocked, reason hook_callback_failed, hookName, exitCode. hook_failure_gate reflète schema, not_block_mode, allow, block, timeout_block sans confondre échec callback et deny permission policy.
La file allow et deny est dans Agent permission policy; le contexte auto evaluation Managed Agents dans évaluation auto Managed Agents; le JSON workflow de session dans Claude Code workflow. Cet article ne couvre que onFailure block 2.1.317, JSON settings hooks et résultat callback, appel Bash, champs audit hook_callback_failed et branches hook_failure_gate.
- Les entrées PreToolUse dans hooks settings exigent onFailure block pour bloquer le tool si callback échoue ; sans block → hook_failure_gate not_block_mode, pas reason hook_callback_failed.
- Le JSON résultat callback exige hookName, onFailure, exitCode entier, timedOut booléen ; exitCode zéro et timedOut false → allow, exitCode non zéro avec onFailure block → block.
- Logs d audit branche block : decision blocked, reason hook_callback_failed, hookName, exitCode ; SIEM agrège par hookName sans mélanger deny permission policy.
- timedOut true et onFailure block → hook_failure_gate timeout_block ; mauvaise forme settings ou JSON callback → schema avant logique block.
Gardez ceci : 2.1.317 change la sémantique block quand un callback hook échoue et les champs audit hook_callback_failed ; il ne réécrit pas les tables allow ou deny d une permission policy. Ensuite : changelog, JSON settings hooks, JSON résultat callback, appel Bash, logs d audit et table, source hook_failure_gate, validation dans JSONNote.
Comment 2.1.317 bloque au lieu de fail-open quand un callback hook échoue
Les notes de version sont dans changelog Claude Code, page tag version v2.1.317. Par rapport à l ancien comportement hook Agent SDK, onFailure block : PreToolUse ou scripts similaires en échec bloquent l action tool au lieu d avertir et continuer.
Le blocage intervient après le callback hook et avant l exécution tool. L audit garde decision blocked, reason hook_callback_failed, hookName, exitCode pour attribuer SIEM au hook en échec, pas un tool denied générique.
Comment écrire PreToolUse et onFailure block dans le JSON settings hooks
Sous hooks dans settings utilisateur ou projet, configurez PreToolUse avec matcher et command et onFailure block sur la même entrée. Dès 2.1.317 seul block explicite bloque le tool si callback échoue ; défaut ou autres valeurs → fail-open ou warn, hook_failure_gate not_block_mode.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "./hooks/pre-bash.sh",
"onFailure": "block"
}
]
}
]
}
}
Casse incorrecte de onFailure ou hooks absent → schema d abord avant block hook_failure_gate. Ne pas confondre forme et not_block_mode.
Comment hookName et exitCode apparaissent dans le JSON résultat callback hook
Après callback hook, le client écrit JSON résultat : hookName nomme l entrée PreToolUse, onFailure reprend block des settings, exitCode est le code de sortie, timedOut marque un kill timeout. Bash allow par permission peut être bloqué si hook échoue.
{
"hookName": "pre-bash.sh",
"onFailure": "block",
"exitCode": 2,
"timedOut": false
}
exitCode entier, timedOut booléen ; hookName absent ou onFailure mal formé → schema. onFailure pas block → not_block_mode.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["hookName", "onFailure", "exitCode", "timedOut"],
"properties": {
"hookName": { "type": "string", "minLength": 1 },
"onFailure": { "type": "string", "enum": ["block", "continue"] },
"exitCode": { "type": "integer" },
"timedOut": { "type": "boolean" }
}
}
Comment le JSON d appel tool Bash avant et après blocage hook
Exemple : permission policy allow Bash ; JSON tool Bash et command npm test. PreToolUse exitCode 2 et onFailure block → pas de Bash, audit decision blocked et reason hook_callback_failed. Garder command et hookName pour branche block hook_failure_gate.
{
"tool": "Bash",
"command": "npm test"
}
{
"hookName": "pre-bash.sh",
"onFailure": "continue",
"exitCode": 2,
"timedOut": false
}
Comment decision blocked et hook_callback_failed apparaissent dans les logs d audit
Callback hook en échec avec onFailure block : audit tool decision blocked, reason hook_callback_failed, hookName, exitCode non zéro ; timedOut true peut ajouter un flag timeout, reason reste hook_callback_failed. Ne pas mélanger deny permission et block hook dans une enum reason.
{
"tool": "Bash",
"decision": "blocked",
"reason": "hook_callback_failed",
"hookName": "pre-bash.sh",
"onFailure": "block",
"exitCode": 2
}
| Signal log ou appel | hook_failure_gate renvoie | Action opérateur |
|---|---|---|
| exitCode 0, timedOut false, onFailure block | allow | Tool autorisé ; pas hook_callback_failed dans le log. |
| exitCode non zéro, onFailure block, timedOut false | block | Aligner sur decision blocked, reason hook_callback_failed, exitCode. |
| onFailure sans block ou valeur warn | not_block_mode | Ne pas écrire hook_callback_failed ; comparer aux rapports fail-open legacy. |
| timedOut true et onFailure block | timeout_block | Logs gardent hook_callback_failed ; SIEM compte timeout_block à part. |
Comment hook_failure_gate choisit entre schema et cinq branches métier
Ordre : schema settings hooks et JSON callback pour onFailure, hookName, exitCode, timedOut ; onFailure pas block → not_block_mode ; timedOut true et block → timeout_block ; exitCode zéro → allow ; exitCode non zéro et block → block ; mauvaise forme → schema.
Ne fusionnez pas block et timeout_block en un hook_failed. Comptez à part not_block_mode fail-open, block exitCode et vrai timeout_block.
def actual_type(data: object) -> str:
if data is None:
return "null"
if isinstance(data, bool):
return "boolean"
if isinstance(data, list):
return "array"
if isinstance(data, int):
return "integer"
if isinstance(data, float):
return "number"
if isinstance(data, str):
return "string"
if isinstance(data, dict):
return "object"
raise TypeError(type(data))
def validate(data: object, schema: dict) -> list[str]:
errors: list[str] = []
if not isinstance(schema, dict):
return errors
types = schema.get("type")
if isinstance(types, str):
types = [types]
actual = actual_type(data)
if types and not any(t == actual or (t == "number" and actual == "integer") for t in types):
errors.append("type")
if "enum" in schema and not any(value == data for value in schema["enum"]):
errors.append("enum")
if isinstance(data, str) and "minLength" in schema and len(data) < schema["minLength"]:
errors.append("minLength")
if isinstance(data, dict) and schema.get("properties"):
for key in schema.get("required") or []:
if key not in data:
errors.append(f"required:{key}")
if schema.get("additionalProperties") is False:
for key in data:
if key not in schema["properties"]:
errors.append(f"additional:{key}")
for key, sub in schema["properties"].items():
if key in data and data[key] is not None:
errors.extend(validate(data[key], sub))
return errors
def hook_failure_gate(ctx: dict, schema: dict) -> str:
if validate(ctx, schema):
return "schema"
mode = ctx.get("onFailure")
if mode != "block":
return "not_block_mode"
if ctx.get("timedOut"):
return "timeout_block"
code = ctx.get("exitCode")
if code == 0:
return "allow"
return "block"
Comment valider settings hooks, résultats callback et logs d audit dans JSONNote
- 1Fixer la forme hooks avec Schema
Ouvrez JSON Schema. Schema settings hooks au premier champ, PreToolUse et onFailure block au second. Erreurs required ou enum d abord ; pas allow ou block si forme mauvaise.
- 2Format pour les appels tool
Ouvrez JSON Format, collez JSON appel tool Bash. Vérifiez tool et command avant block ou allow pour hook_failure_gate.
- 3Diff logs allow et blocked
Ouvrez JSON Diff. À gauche échantillon allow hook réussi, à droite decision blocked et reason hook_callback_failed. hookName surligné est la clé SIEM.
Questions fréquentes
En quoi onFailure block diffère-t-il de l ancien fail-open ?
L ancien loguait l échec hook puis continuait le tool. Avec onFailure block explicite dès 2.1.317, exitCode non zéro ou timeout écrivent decision blocked et reason hook_callback_failed et sautent le tool. Sans block, fail-open et hook_failure_gate not_block_mode.
Comment les logs distinguent exit code 2 des autres codes non zéro ?
Dès 2.1.317 le block dépend de exitCode zéro ou non, pas seulement 2. L audit garde exitCode entier ; SIEM peut séparer 2 de 1 ou 127, hook_failure_gate block traite tout non zéro pareil. Ne pas réduire exitCode à failed booléen.
Quelles valeurs onFailure existent en dehors de block ?
Souvent warn ou défaut fail-open : échec avertit mais exécute le tool. Seul block → decision blocked. not_block_mode pour fail-open ; ne pas logger warn comme hook_callback_failed.
Quel lien entre hook block et la couche permission policy ?
La permission policy décide allow deny ask par tool ; les hooks ajoutent un script avant exécution. Même policy allow Bash, PreToolUse échoué avec onFailure block bloque. JSON séparés ; hook_failure_gate reflète seulement la couche hook.
Cet article est-il un tutoriel d attaque ou de contournement ?
Non. L article explique seulement les formes JSON onFailure block 2.1.317, champs hook_callback_failed et branches hook_failure_gate pour validation et SIEM. Pas d étapes pour contourner les contrôles via échec hook ou exitCode.
Conclusion
Claude Code 2.1.317 prend en charge onFailure block sur les hooks Agent SDK et écrit decision blocked, reason hook_callback_failed, hookName et exitCode dans l audit JSON quand les callbacks échouent. Pilotez hook_failure_gate avec settings hooks, résultats callback, appels Bash et audit blocked, pas seulement permission allow.
Décision : settings ou JSON callback échoue schema → schema ; onFailure pas block → not_block_mode ; exitCode zéro → allow ; exitCode non zéro et block → block ; timedOut true et block → timeout_block ; audits block et timeout_block alignés sur decision blocked, reason hook_callback_failed, hookName, exitCode.
Ensuite : Schema pour settings hooks et résultats callback, Format pour Bash, Diff logs allow et blocked, compter séparément les retours hook_failure_gate.