langchain-ai/deepagents · error · ClientHookStopError

Permission interrupted by hook

Error message

Permission interrupted by hook

What it means

During HITL interrupt processing, if a hook's decision interrupts a permission request, `_process_hitl_interrupts` raises `ClientHookStopError` with the hook-supplied message, defaulting to "Permission interrupted by hook". It signals that a client lifecycle hook, not the user, denied/interrupted the pending permission prompt, ending the headless run.

Source

Thrown at libs/code/deepagents_code/client/non_interactive.py:1860

            [
                ToolCallData(
                    id=f"{interrupt_id}:{index}",
                    name=action_request.get("name", ""),
                    args=action_request.get("args", {}),
                )
                for index, action_request in enumerate(action_requests)
            ]
        )
        if plan.interrupted:
            interrupting = next(
                outcome for outcome in plan.outcomes if outcome.interrupt
            )
            reason = (
                interrupting.decision.get("message")
                if interrupting.decision is not None
                else None
            )
            raise ClientHookStopError(reason or "Permission interrupted by hook")

        if not plan.fully_resolved:
            await state.hooks.notify(
                DcodeNotificationKind.PERMISSION_REQUIRED,
                "Permission required",
            )
        resolved: list[dict[str, str]] = []
        for outcome, action_request in zip(
            plan.outcomes,
            action_requests,
            strict=True,
        ):
            decision = outcome.decision
            resolved.append(
                cast("dict[str, str]", dict(decision))
                if decision is not None
                else _make_hitl_decision(action_request, console)
            )

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Read the hook's `message` if present; if you see the default text, find which registered permission hook interrupted and inspect its logs.
  2. Update the hook to allow the operation or to emit a descriptive `message` in its decision.
  3. Allow-list the needed command/paths via `shell.allow_list` so the permission never reaches the hook.
  4. Catch `ClientHookStopError` in the harness and record it as a policy denial rather than a crash.

Example fix

// before: interrupt with no message
return {"decision": "interrupt"}
// after: explain the denial
return {"decision": "interrupt", "message": "command not on approved list: " + cmd}
Defensive patterns

Strategy: try-catch

Validate before calling

# ensure hooks won't interrupt needed commands before the run
for hook in active_hooks:
    if not hook.allows(command): disable(hook)

Try / catch

from deepagents_code.hooks.client_lifecycle import ClientHookStopError
try:
    await run_non_interactive(task)
except ClientHookStopError as exc:
    record_policy_denial(exc)  # treat as deliberate denial, default message = hook sent none

Prevention

When it happens

Trigger: A permission request is intercepted by a registered hook whose decision interrupts the tool call while running `_run_agent_loop` headless; the interrupting hook's decision contains no `message`, so the default text is used.

Common situations: Policy hooks blocking shell commands not on the allow list; a hook returning an interrupt decision without a custom message; security tooling (e.g. secret-scanning hooks) vetoing file edits in headless CI runs.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/e833ef5a9e1100a9. Report an issue: GitHub.