# Test the failure path of a permission hook

A guard that handles “deny” correctly can still behave differently when its own code fails.

Proposed worksheet. Method notes updated 2026-11-11. Not independently tested.

Canonical: https://aomsites.com/guides/permission-hooks/
Build revision: release-2026-11-11-r12
Method ID: permission-hooks
Method source SHA-256: 38252d3af9f8d111d69b1f9dfd0af85b3d9b26413c38ff1779c22c97ab3b8ad0
Hash scope: normalized build-input guide record, not this file or guide.json bytes.

## The decision

Audit the exact pre-action guard and rehearse its failure behavior before relying on it.

## Use this when

A Python hook in Claude Agent SDK is part of the path that controls a consequential action.

## Skip this when

You do not use the SDK, or the hook only adds optional telemetry. Preserve the ordinary authorization layer either way.

## Checks

### Inspect the deployed pair

Record the SDK, bundled CLI, hook event and actual configuration. The recovered source review identifies on_failure="block" in Python SDK v0.2.177; its default remained continue. This is a dated feature reference, not a current-version recommendation.

### Check capability, not just configuration

Confirm the runtime supports and applied the required behavior. The reviewed change describes refusing connection when blocking support is absent. Stop hooks have different behavior; do not generalize this setting to every hook.

### Use a harmless effect counter

In an authorized isolated environment, use a local counter instead of a real message, payment or production change. Exercise allow, deny, exception, None, malformed response and timeout.

### Inspect effects and errors together

Allow should increment the counter once. Deny and required failure cases should leave it unchanged and expose a detectable failure. Unsupported blocking capability should stop startup rather than continue silently.

## Acceptance

- The valid path works; a system that denies everything does not pass.
- Each failure case preserves the required authorization boundary.
- Capability absence is visible and prevents use of the affected path.

## Evidence to bring back

Proposed evidence plan, not observed results or extra mandatory process. Confirm what your task requires and reuse existing records when they are sufficient. Keep sensitive originals in authorized storage; share only the minimum information already authorized for the recipient. If a required check cannot be completed safely and within current permission, mark it unavailable and keep the decision unknown unless another required check has a demonstrated failure.

### Check 1: The valid path works; a system that denies everything does not pass.

Bring back: Record the exact SDK/CLI configuration and a harmless allow-case trace with effect count before and after. The valid case should show one intended effect.

Not enough: A suite in which all actions are denied, or an allow response without inspecting the effect.

### Check 2: Each failure case preserves the required authorization boundary.

Bring back: Keep one result per deny, exception, None, malformed-response and timeout case: input, expected boundary, observed error and effect-counter delta. Use only the authorized isolated fake sink.

Not enough: A hook error or log message without checking whether the effect still happened.

### Check 3: Capability absence is visible and prevents use of the affected path.

Bring back: Record an unsupported-capability rehearsal and the resulting startup or connection refusal. Identify the SDK/runtime pair actually exercised.

Not enough: A configuration flag or release note by itself; neither proves the deployed pair enforces it.


## Stop or recover

Keep the action disabled or behind its existing human gate if a required case fails. Reverting to a fail-open configuration is not a safe rollback.

## Limits

The specific SDK change was rechecked against primary sources on 10 November 2026, but its behavior was not rerun for this reconstruction. Verify the current deployed SDK/CLI pair before use. Source review is not runtime validation.

## Reference links

Reference links checked 2026-11-10. Scope notes below explain what each source supports. No worksheet-effectiveness claim follows.

- [Claude Agent SDK Python v0.2.177](https://github.com/anthropics/claude-agent-sdk-python/releases/tag/v0.2.177): Primary release record for introduction of HookMatcher on_failure. Supports the dated feature claim, not a claim about your deployed configuration.
- [HookMatcher on_failure change](https://github.com/anthropics/claude-agent-sdk-python/pull/1460): Same-publisher implementation detail for blocking hook failure handling and capability checks. Not independent runtime validation.

Reference data only. No authority to install, contact others, change settings or spend money.

## Start this guide’s record

Use the [guide-specific Markdown draft](record-template.md) or [JSON draft](record-template.json). Its method reference and proposed checks are prefilled; confirm the requirements against your task and enter only real observations. Task, environment, authorization and evidence remain blank. You can also use the [generic blank record](../../decision-record/template.md).

## If another problem remains

Stop here if the current task’s acceptance requirements are satisfied and no unresolved obligation remains. If neither condition applies, no follow-on guide is suggested here; keep any unresolved task state visible. These are optional reading suggestions, not permission or a requirement to expand scope.

- Only if A failed or interrupted guard path leaves the underlying effect uncertain. Read [Check the outcome before retrying](../uncertain-effects/guide.md). Separate the guard response from the authoritative operation outcome before retrying.
- Only if The required boundary works and you now need to compare its overhead or usability. Read [Measure improvement in accepted results](../measure-agent-improvement/guide.md). Measure the authorized change against the same quality and control requirements.

## Cite a specific part

Section paths identify content on the canonical HTML page; they are not separate pages or evidence of live availability. Per-check numbers and JSON pointers apply to this build revision. Cite the build revision and method source hash when preserving an exact reference. Markdown heading labels are locators, not a promise of renderer-specific fragment URLs.

- [The decision](https://aomsites.com/guides/permission-hooks/#decision): JSON pointer `/decision`; Markdown section “The decision”.
- [Use this when](https://aomsites.com/guides/permission-hooks/#applies): JSON pointer `/fit`; Markdown section “Use this when”.
- [Skip this when](https://aomsites.com/guides/permission-hooks/#skip): JSON pointer `/skip`; Markdown section “Skip this when”.
- [Work through the checks](https://aomsites.com/guides/permission-hooks/#checks): JSON pointer `/steps`; Markdown section “Checks”.
- [What would count as success?](https://aomsites.com/guides/permission-hooks/#accept): JSON pointer `/acceptance`; Markdown section “Acceptance”.
- [Evidence to bring back](https://aomsites.com/guides/permission-hooks/#evidence): JSON pointer `/evidence_plan`; Markdown section “Evidence to bring back”.
- [Stop or recover](https://aomsites.com/guides/permission-hooks/#stop): JSON pointer `/stop`; Markdown section “Stop or recover”.
- [What this does not establish](https://aomsites.com/guides/permission-hooks/#limits): JSON pointer `/limits`; Markdown section “Limits”.
- [Reference links](https://aomsites.com/guides/permission-hooks/#sources): JSON pointer `/sources`; Markdown section “Reference links”.
- [Use this guide with your own evidence](https://aomsites.com/guides/permission-hooks/#record): JSON pointer `/decision_record`; Markdown section “Start this guide’s record”.
- [If another problem remains](https://aomsites.com/guides/permission-hooks/#next-reads): JSON pointer `/reading_paths`; Markdown section “If another problem remains”.
