langchain-ai/deepagents · error · ValueError

PreCompact requires a stable tool-call identity

Error message

PreCompact requires a stable tool-call identity

What it means

Hook invocation ids must be stable across retries so events can be deduplicated. PreCompactEvent has no tool call to derive an id from, so it depends on a caller-supplied logical_event_id; when neither a tool-call id nor a logical id exists, _logical_event_identity raises ValueError because no stable identity can be formed.

Source

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

        | PostToolUseFailureEvent
        | PreCompactEvent
        | StopEvent
        | SubagentStartEvent
        | SubagentStopEvent
    ),
    *,
    logical_event_id: str | None = None,
) -> str:
    if isinstance(
        event,
        PreToolUseEvent | PostToolUseEvent | PostToolUseFailureEvent,
    ):
        return event.call.id
    if isinstance(event, PreCompactEvent):
        if logical_event_id:
            return logical_event_id
        msg = "PreCompact requires a stable tool-call identity"
        raise ValueError(msg)
    if isinstance(event, SubagentStartEvent):
        return event.agent.id
    if isinstance(event, SubagentStopEvent):
        return f"{event.agent.id}:{event.continuation_count}"
    message_hash = hashlib.sha256(event.last_assistant_message.encode()).hexdigest()
    return f"{event.continuation_count}:{message_hash}"


def _config_thread_id(config: Mapping[str, Any] | None) -> str | None:
    if not isinstance(config, Mapping):
        return None
    configurable = config.get("configurable")
    if not isinstance(configurable, Mapping):
        return None
    value = configurable.get("thread_id")
    return value if isinstance(value, str) and value else None

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Pass a logical_event_id when invoking pre-compact hooks — derive it from the compaction request (e.g. a uuid or thread/turn composite key).
  2. Use the standard compaction flow, which assigns the logical id automatically.
  3. If building events in tests, supply event.call.id or a logical_event_id to satisfy identity computation.
  4. Catch ValueError and treat it as a programming error: regenerate the event with a stable id rather than retrying.

Example fix

// before
await middleware._invoke_hook(gate, PreCompactEvent(...), config, deadline)  # ValueError

// after
await middleware._invoke_hook(
    gate, PreCompactEvent(...), config, deadline,
    logical_event_id=f"compact:{thread_id}:{turn}",
)
Defensive patterns

Strategy: validation

Validate before calling

if isinstance(event, PreCompactEvent) and not logical_event_id:
    raise ValueError("pass logical_event_id for PreCompact hook invocations")

Try / catch

try:
    run_compact_hooks(event, ...)
except ValueError as exc:
    if "stable tool-call identity" in str(exc):
        rerun_with_logical_event_id()

Prevention

When it happens

Trigger: A PreCompactEvent reaches _invocation_id/_logical_event_identity with logical_event_id=None and no other stable key: triggering pre-compact hooks through a path that doesn't assign a logical event id (e.g. a manual or custom compaction trigger outside the standard compact flow).

Common situations: Custom compaction pipelines firing PreCompactEvent without generating a logical_event_id; replay/retry logic that drops the logical id; tests constructing PreCompactEvent directly and invoking the middleware.

Related errors


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