{"record":{"id":"9105726fb38d1344","repo":"langchain-ai/deepagents","slug":"offload-hook-request-has-no-invocation-id","errorCode":null,"errorMessage":"Offload hook request has no invocation id.","messagePattern":"Offload hook request has no invocation id\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/client/remote_client.py","lineNumber":448,"sourceCode":"            if not isinstance(response, dict):\n                msg = \"Offload server returned a non-object response.\"\n                raise TypeError(msg)\n            status = response.get(\"status\")\n            if status == \"complete\":\n                return _validated_offload_result(response.get(\"result\"))\n            request = response.get(\"request\")\n            if status != \"interrupt\" or not is_hook_interrupt_payload(request):\n                msg = \"Offload server returned an invalid operation response.\"\n                raise RuntimeError(msg)\n            invocation = request.get(\"request\")\n            invocation_id = (\n                invocation.get(\"invocation_id\")\n                if isinstance(invocation, dict)\n                else None\n            )\n            if not isinstance(invocation_id, str) or not invocation_id:\n                msg = \"Offload hook request has no invocation id.\"\n                raise RuntimeError(msg)\n            logger.debug(\n                \"Offload round %d fulfilling hook invocation %s\",\n                round_index,\n                invocation_id,\n            )\n            if round_index == _OFFLOAD_MAX_RESUME_ROUNDS:\n                # The extra iteration exists to POST the last fulfillment and\n                # read the result, not to answer one more hook. Without this the\n                # loop fulfills 33 hooks and then reports \"after 32 rounds\".\n                break\n            hook_responses[invocation_id] = await _await_offload_step(\n                fulfill_hook(request),\n                graph=graph,\n                thread_id=thread_id,\n                operation_id=operation_id,\n            )\n        # The server only re-requests an invocation id it has not been given, so\n        # exhaustion means this many *distinct* ids. That is either genuinely","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/client/remote_client.py#L430-L466","documentation":"Inside an `interrupt` response, the hook request's inner `request` object must carry a non-empty string `invocation_id` — the key the client uses to key its fulfillment into `hook_responses` for the next round-trip. When it is absent, empty, or not a string, the client raises this RuntimeError because fulfillment could not be correlated.","triggerScenarios":"Server sends an interrupt whose `request.request` is not a dict, is missing `invocation_id`, or has `invocation_id` set to null/empty/non-string.","commonSituations":"A custom server's hook executor that forgets to assign invocation ids; server/client schema drift where the id field was renamed (e.g. `id` vs `invocation_id`); partially-constructed interrupt payloads after a server error.","solutions":["Upgrade or fix the server so every hook interrupt includes a non-empty string `invocation_id`.","Log the failing interrupt payload (`request`) to confirm the field name and type the server actually emits.","If a rename occurred, align the server's field name with the client's expected `invocation_id` (or vice versa in a matched release pair).","Retry the operation once — a transient partial payload from a crashing server may resolve on a fresh offload."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def has_invocation_id(hook_request: object) -> bool:\n    inner = hook_request.get(\"request\") if isinstance(hook_request, dict) else None\n    return isinstance(inner, dict) and isinstance(inner.get(\"invocation_id\"), str) and bool(inner[\"invocation_id\"])","tryCatchPattern":"try:\n    result = await agent.aoffload(config=config, context=ctx, fulfill_hook=hook)\nexcept RuntimeError as exc:\n    if \"no invocation id\" in str(exc):\n        logging.error(\"Server sent a hook interrupt without invocation_id; fix server hook executor\")\n    else:\n        raise","preventionTips":["Ensure server-side hook executors always assign a non-empty invocation_id.","Add a server-side schema check on hook interrupts before they are sent.","Keep field names (`invocation_id`) identical across server and client releases."],"tags":["protocol","offload","hooks","invocation-id"],"backgroundTag":"invalid-server-response-shape","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}