langchain-ai/deepagents · error · ValueError

Hook resume snapshot_id mismatch: expected {snapshot_id}, go

Error message

Hook resume snapshot_id mismatch: expected {snapshot_id}, got {response.snapshot_id}

What it means

parse_hook_resume_value also verifies that the resume payload's snapshot_id matches the snapshot captured when the hook interrupt was raised. snapshot_id ties the resume to the exact state the hook saw; a mismatch means the agent state has changed since the interrupt (new snapshot taken) and the stale resume is rejected with ValueError.

Source

Thrown at libs/code/deepagents_code/hooks/interrupt.py:113

    Returns:
        Validated response.

    Raises:
        ValueError: If the resume payload is missing, mistyped, or mismatched.
    """
    response = HOOK_INVOCATION_RESPONSE_ADAPTER.validate_python(value)
    if response.invocation_id != invocation_id:
        msg = (
            f"Hook resume invocation_id mismatch: expected {invocation_id}, "
            f"got {response.invocation_id}"
        )
        raise ValueError(msg)
    if response.snapshot_id != snapshot_id:
        msg = (
            f"Hook resume snapshot_id mismatch: expected {snapshot_id}, "
            f"got {response.snapshot_id}"
        )
        raise ValueError(msg)
    return response


def is_hook_interrupt_payload(value: object) -> bool:
    """Return whether `value` looks like a Hooks v2 invocation interrupt."""
    return (
        isinstance(value, dict) and value.get("type") == HOOK_INVOCATION_INTERRUPT_TYPE
    )

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Obtain a fresh interrupt payload and use its snapshot_id and invocation_id together when resuming
  2. Discard resume values saved before the latest checkpoint and re-answer the hook prompt
  3. When replaying checkpoints, replay the corresponding hook resume from the same checkpoint, not a newer one
  4. If this fires consistently after retries, verify the runtime preserves snapshot ids across graph re-entry or upgrade the runtime

Example fix

// before
resume = parse_hook_resume_value(old_payload, invocation_id, current_snapshot_id)

// after
payload = pending_interrupts[invocation_id]  # payload carries its own snapshot_id
resume = parse_hook_resume_value(payload.resume_value, invocation_id, payload.snapshot_id)
Defensive patterns

Strategy: validation

Validate before calling

if resume_payload.get("snapshot_id") != expected_snapshot_id:
    raise ValueError("resume references an outdated snapshot; re-answer the hook")

Type guard

def matches_snapshot(payload: dict, snapshot_id: str) -> bool:
    return payload.get("snapshot_id") == snapshot_id

Try / catch

try:
    resume = parse_hook_resume_value(payload, invocation_id, snapshot_id)
except ValueError as exc:
    logger.error("snapshot mismatch: %s", exc)
    resume = prompt_user_again(invocation_id)  # discard stale answer

Prevention

When it happens

Trigger: Resuming a hook interrupt whose payload references a snapshot_id different from the expected one — typically after the agent checkpointed a new snapshot between the original interrupt and the resume, or when replaying a resume value saved from a previous run.

Common situations: Time-travel/checkpoint replay in langgraph-based flows resuming from an older snapshot; stale TUI state after graph re-invocation; multiple pending interrupts resolved out of order so payload/snapshot pairs get crossed.

Related errors


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