langchain-ai/deepagents · error · RuntimeError
Offload hook request has no invocation id.
Error message
Offload hook request has no invocation id.
What it means
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.
Source
Thrown at libs/code/deepagents_code/client/remote_client.py:448
if not isinstance(response, dict):
msg = "Offload server returned a non-object response."
raise TypeError(msg)
status = response.get("status")
if status == "complete":
return _validated_offload_result(response.get("result"))
request = response.get("request")
if status != "interrupt" or not is_hook_interrupt_payload(request):
msg = "Offload server returned an invalid operation response."
raise RuntimeError(msg)
invocation = request.get("request")
invocation_id = (
invocation.get("invocation_id")
if isinstance(invocation, dict)
else None
)
if not isinstance(invocation_id, str) or not invocation_id:
msg = "Offload hook request has no invocation id."
raise RuntimeError(msg)
logger.debug(
"Offload round %d fulfilling hook invocation %s",
round_index,
invocation_id,
)
if round_index == _OFFLOAD_MAX_RESUME_ROUNDS:
# The extra iteration exists to POST the last fulfillment and
# read the result, not to answer one more hook. Without this the
# loop fulfills 33 hooks and then reports "after 32 rounds".
break
hook_responses[invocation_id] = await _await_offload_step(
fulfill_hook(request),
graph=graph,
thread_id=thread_id,
operation_id=operation_id,
)
# The server only re-requests an invocation id it has not been given, so
# exhaustion means this many *distinct* ids. That is either genuinelyView on GitHub (pinned to a1af029e6e)
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.
Defensive patterns
Strategy: type-guard
Type guard
def has_invocation_id(hook_request: object) -> bool:
inner = hook_request.get("request") if isinstance(hook_request, dict) else None
return isinstance(inner, dict) and isinstance(inner.get("invocation_id"), str) and bool(inner["invocation_id"]) Try / catch
try:
result = await agent.aoffload(config=config, context=ctx, fulfill_hook=hook)
except RuntimeError as exc:
if "no invocation id" in str(exc):
logging.error("Server sent a hook interrupt without invocation_id; fix server hook executor")
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Offload server returned an invalid operation response.
- Offload did not complete after {_OFFLOAD_MAX_RESUME_ROUNDS}
- Offload server returned an invalid cancellation acknowledgem
- Offload server completed without a typed result.
- Offload result has no status.
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/9105726fb38d1344.
Report an issue: GitHub.