{"record":{"id":"f2e9f80fc9e03ce3","repo":"langchain-ai/deepagents","slug":"hook-resume-snapshot-id-mismatch-expected-snapsh","errorCode":null,"errorMessage":"Hook resume snapshot_id mismatch: expected {snapshot_id}, got {response.snapshot_id}","messagePattern":"Hook resume snapshot_id mismatch: expected (.+?), got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/hooks/interrupt.py","lineNumber":113,"sourceCode":"    Returns:\n        Validated response.\n\n    Raises:\n        ValueError: If the resume payload is missing, mistyped, or mismatched.\n    \"\"\"\n    response = HOOK_INVOCATION_RESPONSE_ADAPTER.validate_python(value)\n    if response.invocation_id != invocation_id:\n        msg = (\n            f\"Hook resume invocation_id mismatch: expected {invocation_id}, \"\n            f\"got {response.invocation_id}\"\n        )\n        raise ValueError(msg)\n    if response.snapshot_id != snapshot_id:\n        msg = (\n            f\"Hook resume snapshot_id mismatch: expected {snapshot_id}, \"\n            f\"got {response.snapshot_id}\"\n        )\n        raise ValueError(msg)\n    return response\n\n\ndef is_hook_interrupt_payload(value: object) -> bool:\n    \"\"\"Return whether `value` looks like a Hooks v2 invocation interrupt.\"\"\"\n    return (\n        isinstance(value, dict) and value.get(\"type\") == HOOK_INVOCATION_INTERRUPT_TYPE\n    )\n","sourceCodeStart":95,"sourceCodeEnd":122,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/hooks/interrupt.py#L95-L122","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Obtain a fresh interrupt payload and use its snapshot_id and invocation_id together when resuming","Discard resume values saved before the latest checkpoint and re-answer the hook prompt","When replaying checkpoints, replay the corresponding hook resume from the same checkpoint, not a newer one","If this fires consistently after retries, verify the runtime preserves snapshot ids across graph re-entry or upgrade the runtime"],"exampleFix":"// before\nresume = parse_hook_resume_value(old_payload, invocation_id, current_snapshot_id)\n\n// after\npayload = pending_interrupts[invocation_id]  # payload carries its own snapshot_id\nresume = parse_hook_resume_value(payload.resume_value, invocation_id, payload.snapshot_id)","handlingStrategy":"validation","validationCode":"if resume_payload.get(\"snapshot_id\") != expected_snapshot_id:\n    raise ValueError(\"resume references an outdated snapshot; re-answer the hook\")\n","typeGuard":"def matches_snapshot(payload: dict, snapshot_id: str) -> bool:\n    return payload.get(\"snapshot_id\") == snapshot_id\n","tryCatchPattern":"try:\n    resume = parse_hook_resume_value(payload, invocation_id, snapshot_id)\nexcept ValueError as exc:\n    logger.error(\"snapshot mismatch: %s\", exc)\n    resume = prompt_user_again(invocation_id)  # discard stale answer\n","preventionTips":["Treat invocation_id + snapshot_id as an inseparable pair when storing resumes","After a checkpoint/time-travel, regenerate hook resumes from the checkpointed state","Never replay resume payloads recorded before the latest snapshot"],"tags":["hooks","interrupt","resume","snapshot","identity-check"],"backgroundTag":"hook-resume-id-mismatch","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}