langchain-ai/deepagents · error · ClientHookStopError

Compact session start stopped by hook

Error message

Compact session start stopped by hook

What it means

After a headless compaction, `_after_headless_compact` fires `on_session_start` with cause `COMPACT`; if the hook outcome is not ok, it raises `ClientHookStopError` using the hook's `stop_reason` or this default message. This lets hooks veto continuing the session after context compaction.

Source

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

        isinstance(message, ToolMessage)
        and getattr(message, "name", None) == "compact_conversation"
        and str(message.content).startswith("Conversation compacted.")
    ):
        return None
    tool_call_id = getattr(message, "tool_call_id", None)
    return tool_call_id if isinstance(tool_call_id, str) and tool_call_id else None


async def _after_headless_compact(state: StreamState) -> None:
    from deepagents_code.hooks.client_lifecycle import ClientHookStopError
    from deepagents_code.hooks.models.domain import SessionStartCause

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


async def _end_headless_session(
    state: StreamState,
    cause: SessionEndCause,
    *,
    timeout_seconds: float = SESSION_END_DRAIN_TIMEOUT_SECONDS,
) -> None:
    """End a headless session without letting Hooks v2 stall process exit.

    Args:
        state: Active headless stream state.
        cause: Reason the session ended.
        timeout_seconds: Maximum time to wait for `SessionEnd` handlers.
    """
    if state.session_end_fired:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Inspect the hook's `stop_reason`; if you only see the default text, identify the hook handling `SessionStartCause.COMPACT` and fix or disable it.
  2. Handle the COMPACT cause explicitly in the hook and return ok when continuation is intended.
  3. Catch `ClientHookStopError` around the headless run and finalize session state via the session-end path.
  4. Test hooks against all `SessionStartCause` values (STARTUP, COMPACT).

Example fix

// before
async def on_session_start(cause, model): return HookOutcome(ok=False)
// after
async def on_session_start(cause, model):
    if cause is SessionStartCause.COMPACT:
        return HookOutcome(ok=True)
    return check_policy(model)
Defensive patterns

Strategy: try-catch

Try / catch

from deepagents_code.hooks.client_lifecycle import ClientHookStopError
try:
    await _stream_agent(...)
except ClientHookStopError as exc:
    if not exc.args[0] or exc.args[0] == "Compact session start stopped by hook":
        find_hook_missing_compact_handling()  # hook returned not-ok without a reason

Prevention

When it happens

Trigger: A compaction occurs during `_stream_agent` and a registered client hook returns `ok=False` from `on_session_start(SessionStartCause.COMPACT)` without a `stop_reason`, producing this default message.

Common situations: Budget or policy hooks rejecting post-compaction continuation; hooks that only allow fresh sessions (not COMPACT restarts); a hook implementation bug returning not-ok on the COMPACT cause it did not handle.

Related errors


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