langchain-ai/deepagents · warning · ClientHookStopError

User prompt submission stopped by hook

Error message

User prompt submission stopped by hook

What it means

When a UserPromptSubmit hook is registered, execute_task_textual awaits hooks.on_user_prompt before running the agent; if the hook returns an unsuccessful outcome, a ClientHookStopError is raised using the hook's stop_reason (defaulting to this message). This lets user hooks veto prompts before any model call.

Source

Thrown at libs/code/deepagents_code/tui/textual_adapter.py:1829

            user_msg.update(message_kwargs)
        additional_kwargs = user_msg.get("additional_kwargs")
        trusted_kwargs = (
            dict(additional_kwargs) if isinstance(additional_kwargs, dict) else {}
        )
        trusted_kwargs[USER_PROMPT_METADATA_KEY] = user_prompt_metadata(
            user_input,
            [str(path) for path in mentioned_files],
            turn_id=turn_id,
        )
        user_msg["additional_kwargs"] = trusted_kwargs
        messages: list[dict[str, Any]] = []
        transcript.append([HumanMessage(content=message_content or "")])
        if hooks.has_handlers(HookEvent.USER_PROMPT_SUBMIT):
            prompt_outcome = await hooks.on_user_prompt(user_input)
            if not prompt_outcome.ok:
                from deepagents_code.hooks.client_lifecycle import ClientHookStopError

                raise ClientHookStopError(
                    prompt_outcome.stop_reason
                    or "User prompt submission stopped by hook"
                )
        else:
            prompt_outcome = PromptOutcome()
            await dispatch_hook("session.start", {"thread_id": thread_id})
            await dispatch_hook("user.prompt", {})
        session_context = hooks.take_pending_context(thread_id=thread_id)
        if session_context:
            messages.append({"role": "system", "content": "\n\n".join(session_context)})
        if prompt_outcome.context:
            messages.append(
                {"role": "system", "content": "\n\n".join(prompt_outcome.context)}
            )
        if not prompt_outcome.suppress_original_prompt:
            messages.append(user_msg)
        stream_input: dict | Command = {
            "messages": messages,

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Read prompt_outcome.stop_reason / the raised message to see why the hook rejected the prompt and adjust the input accordingly.
  2. Fix or relax the hook script so legitimate prompts pass.
  3. Temporarily disable the USER_PROMPT_SUBMIT hook in settings if it is misbehaving.
  4. Handle ClientHookStopError in the caller to show the stop reason instead of a generic failure.

Example fix

// before
await execute_task_textual(adapter, user_input)  # raises ClientHookStopError
// after
try:
    await execute_task_textual(adapter, user_input)
except ClientHookStopError as exc:
    adapter._update_status(f"Prompt blocked by hook: {exc}")
Defensive patterns

Strategy: try-catch

Try / catch

try:
    await execute_task_textual(adapter, user_input)
except ClientHookStopError as exc:
    show_status(f"Prompt blocked by hook: {exc}")

Prevention

When it happens

Trigger: A configured user_prompt_submit hook returns ok=false for the submitted prompt; the submission then aborts with ClientHookStopError carrying the hook's stop_reason.

Common situations: Project hook scripts that block prompts matching policy rules (e.g. secrets, forbidden commands); hooks that fail validation and intentionally stop the turn; misconfigured hooks returning a failed status on valid input.

Related errors


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