langchain-ai/deepagents · error · RuntimeError

hooks_snapshot_id is required to emit server-owned hook even

Error message

hooks_snapshot_id is required to emit server-owned hook events

What it means

Server-owned hook events (progress, audit) are keyed by a hooks_snapshot_id carried in the session gate/context. Without it the middleware cannot attribute emitted events to a validated hook snapshot, so _invoke_hook raises RuntimeError rather than emitting events with unknown provenance. This indicates the middleware was driven outside a properly snapshot-initialized session.

Source

Thrown at libs/code/deepagents_code/hooks/server_middleware.py:912

    context: HookContext,
    event: (
        PreToolUseEvent
        | PostToolUseEvent
        | PostToolUseFailureEvent
        | PreCompactEvent
        | StopEvent
        | SubagentStartEvent
        | SubagentStopEvent
    ),
    *,
    gate: _SessionHookGate | None,
    config: Mapping[str, Any] | None,
    deadline: timedelta,
    logical_event_id: str | None = None,
) -> HookDecision:
    if gate is None:
        msg = "hooks_snapshot_id is required to emit server-owned hook events"
        raise RuntimeError(msg)
    run_id = _run_id(config, context.thread_id)
    invocation_id = _invocation_id(
        snapshot_id=gate["snapshot_id"],
        context=context,
        event=event,
        logical_event_id=logical_event_id,
    )
    request = HookInvocationRequest(
        protocol_version=1,
        invocation_id=invocation_id,
        snapshot_id=gate["snapshot_id"],
        run_id=run_id,
        invocation=HookInvocation(context=context, event=event),
        deadline=datetime.now(UTC) + deadline,
    )
    operation_responses = _HOOK_RESPONSES.get()
    if operation_responses is None:
        raw = interrupt(build_hook_interrupt_payload(request))

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Ensure the agent is created/run through the standard entry point that builds a hook snapshot and injects hooks_snapshot_id into the runtime context (e.g. the server session bootstrap).
  2. If constructing config manually, include the hooks_snapshot_id produced by HooksSnapshot.from_config/create in the context fields.
  3. Update client/server together so both sides agree on snapshot propagation.
  4. Catch RuntimeError during development and assert gate presence to fail fast on misconfiguration.

Example fix

// before
config = {"configurable": {"thread_id": tid}}
agent.invoke(state, config)  # RuntimeError: no hooks_snapshot_id

// after
snapshot = HooksSnapshot.create(config_with_hooks)
config = {"configurable": {"thread_id": tid, "hooks_snapshot_id": snapshot.snapshot_id}}
agent.invoke(state, config)
Defensive patterns

Strategy: validation

Validate before calling

gate = _session_gate(runtime_context)
if gate is None or not gate.get("snapshot_id"):
    raise RuntimeError("run agent through a snapshot-initialized session")

Type guard

def has_hooks_snapshot(context: Mapping[str, Any]) -> TypeGuard[Mapping[str, Any]]:
    return isinstance(context.get("hooks_snapshot_id"), str) and bool(context["hooks_snapshot_id"])

Try / catch

try:
    agent.invoke(state, config)
except RuntimeError as exc:
    if "hooks_snapshot_id" in str(exc):
        reinitialize_session_with_snapshot()

Prevention

When it happens

Trigger: Calling _invoke_hook (via any of _maybe_subagent_start, _after_model, _maybe_post_tool_use, _maybe_subagent_stop, _after_agent, _pre_auto_compact) when the resolved session gate is None — i.e. the runtime context/config carries no hooks_snapshot_id field.

Common situations: Invoking the agent graph directly with a hand-built config that omits hooks snapshot metadata; running a stale client against a newer server that requires snapshot ids; constructing the middleware without going through the snapshot-creation entry point.

Related errors


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