langchain-ai/deepagents · warning · ClientHookStopError

Compact session start stopped by hook

Error message

Compact session start stopped by hook

What it means

After an automatic context compaction, _after_automatic_compact fires the SessionStart hook with cause COMPACT. If the hook rejects (outcome.ok is false), a ClientHookStopError is raised with the hook's stop_reason or this default, halting the resumed stream because the post-compact session start was vetoed.

Source

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

    recover_interrupted_turn = not (
        graph_input is not None and graph_input.get("goal_criteria_request") is not None
    )

    # Track summarization lifecycle so spinner status and notification stay in sync.
    summarization_in_progress = False
    completed_compaction_ids: set[str] = set()

    async def _after_automatic_compact() -> None:
        from deepagents_code.config import runtime_state
        from deepagents_code.hooks.client_lifecycle import ClientHookStopError
        from deepagents_code.hooks.models.domain import SessionStartCause

        outcome = await hooks.on_session_start(
            SessionStartCause.COMPACT,
            model=runtime_state.model_name or None,
        )
        if not outcome.ok:
            raise ClientHookStopError(
                outcome.stop_reason or "Compact session start stopped by hook"
            )

    stream_completed = False
    try:
        while True:
            interrupt_occurred = False
            suppress_resumed_output = False
            pending_interrupts: dict[str, tuple[tuple[Any, ...], HITLRequest]] = {}
            pending_ask_user: dict[str, AskUserRequest] = {}
            pending_hook_resumes: dict[str, dict[str, Any]] = {}

            if context is None:
                context = CLIContext()
            context["thread_id"] = thread_id
            if turn_id is not None:
                context["turn_id"] = turn_id
            else:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Update the session_start hook to accept SessionStartCause.COMPACT.
  2. Check the hook's stop_reason output to see why it denied the compact restart and adjust policy.
  3. Handle ClientHookStopError around execute_task_textual to surface the reason gracefully.
  4. Temporarily disable the session_start hook to unblock the session.

Example fix

// before
# hook: only allow startup
if cause != "startup": return deny("unsupported cause")
// after
if cause not in ("startup", "resume", "compact"):
    return deny("unsupported cause")
Defensive patterns

Strategy: try-catch

Validate before calling

if hooks.has_handlers(HookEvent.SESSION_START):
    probe = await hooks.on_session_start(SessionStartCause.COMPACT, model=model)
    if not probe.ok:
        disable_compaction_hooks()

Try / catch

try:
    await execute_task_textual(adapter, user_input)
except ClientHookStopError as exc:
    logger.warning("post-compact session start denied: %s", exc)
    recover_session()

Prevention

When it happens

Trigger: Automatic compaction triggers mid-conversation and the registered session_start hook returns ok=false for the COMPACT cause — the turn aborts right after compaction.

Common situations: Session-start hooks that only permit 'startup'/'resume' causes but not 'compact'; a hook script erroring on the compact payload; policy hooks that deny session starts in certain states.

Related errors


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