{"record":{"id":"f86831cba8c5a328","repo":"langchain-ai/deepagents","slug":"received-hook-invocation-interrupt-without-a-hooks","errorCode":null,"errorMessage":"Received hook invocation interrupt without a HooksRuntime","messagePattern":"Received hook invocation interrupt without a HooksRuntime","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/hooks/manager.py","lineNumber":537,"sourceCode":"    async def fulfill_interrupt(self, payload: object) -> dict[str, object]:\n        \"\"\"Execute one server-owned hook interrupt on the client runtime.\n\n        Args:\n            payload: Raw LangGraph interrupt value.\n\n        Returns:\n            Resume value for `Command(resume=...)`.\n\n        Raises:\n            RuntimeError: If hooks are unavailable, or the payload is not a\n                parseable hook invocation.\n        \"\"\"\n        from deepagents_code.hooks.client import fulfill_hook_interrupt\n\n        runtime = self._runtime\n        if runtime is None:\n            msg = \"Received hook invocation interrupt without a HooksRuntime\"\n            raise RuntimeError(msg)\n        resume = await fulfill_hook_interrupt(runtime, payload)\n        if resume is None:\n            msg = \"Failed to parse hook interrupt\"\n            raise RuntimeError(msg)\n        return resume\n\n    async def fulfill_pending_interrupts(\n        self,\n        pending: Mapping[str, object],\n    ) -> dict[str, dict[str, object]]:\n        \"\"\"Execute a batch of server-owned hook interrupts.\n\n        Args:\n            pending: LangGraph interrupt id to raw interrupt payload.\n\n        Returns:\n            Resume values keyed by interrupt id.\n","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/hooks/manager.py#L519-L555","documentation":"HookManager.fulfill_interrupt resolves a Hooks v2 invocation interrupt by delegating to a HooksRuntime via fulfill_hook_interrupt. If the manager was constructed without a runtime (self._runtime is None), there is no engine able to execute the hook callback, so it raises RuntimeError rather than silently failing.","triggerScenarios":"Calling fulfill_interrupt (e.g. from execute_task_textual) on a HookManager whose HooksRuntime was never attached — such as a manager created before runtime initialization, or one running in a mode where hooks are disabled.","commonSituations":"Resuming an agent graph that contains pending hook interrupts while the client-side runtime isn't wired (misconfiguration, runtime not started, or a long-lived manager whose runtime was closed); replaying persisted thread state with hook interrupts in a fresh process where hooks weren't configured.","solutions":["Ensure the HooksRuntime is attached to the manager (build/set the runtime via _build_service's configuration path) before resuming","Complete runtime initialization before calling execute/fulfill paths — hooks require the client hook service to be configured","If hooks are intentionally unused, clear the pending hook interrupts instead of fulfilling them","Check process startup/config so the hook runtime is constructed in the same process that fulfills interrupts"],"exampleFix":"// before\nmanager = HookManager(...)  # no runtime attached\nawait manager.fulfill_interrupt(payload)\n\n// after\nmanager = HookManager(...)\nmanager.set_runtime(HooksRuntime(config=hook_config))  # attach runtime first\nawait manager.fulfill_interrupt(payload)","handlingStrategy":"fallback","validationCode":"if manager._runtime is None:  # prefer a public accessor if available\n    raise RuntimeError(\"hooks runtime not initialized; cannot fulfill hook interrupt\")\n","typeGuard":"def can_fulfill_hooks(manager) -> bool:\n    return manager._runtime is not None\n","tryCatchPattern":"try:\n    resume = await manager.fulfill_interrupt(payload)\nexcept RuntimeError as exc:\n    if \"without a HooksRuntime\" in str(exc):\n        manager.set_runtime(build_hooks_runtime(hook_config))\n        resume = await manager.fulfill_interrupt(payload)\n    else:\n        raise\n","preventionTips":["Initialize and attach the HooksRuntime before executing/resuming any graph that uses hooks","Use one hook configuration everywhere so interrupts are only raised when a runtime exists","Add a startup assertion that the runtime is attached when hooks are enabled"],"tags":["hooks","runtime","initialization","interrupt"],"backgroundTag":"missing-hooks-runtime","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}