{"record":{"id":"6e264b7c0b9d97d3","repo":"langchain-ai/deepagents","slug":"hook-process-pipes-are-unavailable","errorCode":null,"errorMessage":"Hook process pipes are unavailable","messagePattern":"Hook process pipes are unavailable","errorType":"exception","errorClass":"BrokenPipeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/hooks/runner.py","lineNumber":221,"sourceCode":"    completion = asyncio.gather(task, return_exceptions=True)\n    try:\n        await asyncio.wait_for(\n            asyncio.shield(completion),\n            timeout=_TERMINATE_WAIT_TIMEOUT,\n        )\n    except TimeoutError:\n        task.cancel()\n        await completion\n\n\nasync def _communicate_bounded(\n    process: Process,\n    payload: bytes,\n    limit: int,\n) -> tuple[bytes, bytes, bool, bool]:\n    if process.stdin is None or process.stdout is None or process.stderr is None:\n        msg = \"Hook process pipes are unavailable\"\n        raise BrokenPipeError(msg)\n    stdout_task = asyncio.create_task(_read_bounded(process.stdout, limit))\n    stderr_task = asyncio.create_task(_read_bounded(process.stderr, limit))\n    stdin_task = asyncio.create_task(_write_input(process, payload))\n    try:\n        await process.wait()\n        stdout, stderr = await asyncio.gather(stdout_task, stderr_task)\n        with contextlib.suppress(BrokenPipeError, ConnectionResetError):\n            await stdin_task\n    except BaseException:\n        stdin_task.cancel()\n        stdout_task.cancel()\n        stderr_task.cancel()\n        await asyncio.gather(\n            stdin_task,\n            stdout_task,\n            stderr_task,\n            return_exceptions=True,\n        )","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/hooks/runner.py#L203-L239","documentation":"`_communicate_bounded` writes the hook payload to the subprocess and reads bounded stdout/stderr over asyncio pipes. If any of the process's stdin/stdout/stderr streams is None, it raises `BrokenPipeError` — the subprocess was created without the piped streams the hook runner requires, so payload exchange is impossible.","triggerScenarios":"`run_command_handler` spawned the hook `Process` without `stdin=PIPE`, `stdout=PIPE`, `stderr=PIPE` (or the streams were closed/None), then called `_communicate_bounded` on it.","commonSituations":"Custom/forked runner code creating the subprocess with inherited or devnull streams; process created by a different code path and passed in; platform/asyncio edge cases where stream objects are None before/after process exit.","solutions":["Create the hook process with `asyncio.create_subprocess_exec(..., stdin=PIPE, stdout=PIPE, stderr=PIPE)` so all three pipes exist","Use the library's own process-creation path in `run_command_handler` rather than supplying an externally created Process","If passing a Process across code paths, assert its stdin/stdout/stderr are non-None before communicating"],"exampleFix":"// before\nproc = await asyncio.create_subprocess_exec(*argv, stdout=DEVNULL)\n// after\nproc = await asyncio.create_subprocess_exec(*argv, stdin=PIPE, stdout=PIPE, stderr=PIPE)","handlingStrategy":"try-catch","validationCode":"if proc.stdin is None or proc.stdout is None or proc.stderr is None:\n    raise BrokenPipeError(\"hook process must be created with stdin/stdout/stderr=PIPE\")","typeGuard":"def has_pipes(p: asyncio.subprocess.Process) -> bool:\n    return p.stdin is not None and p.stdout is not None and p.stderr is not None","tryCatchPattern":"try:\n    stdout, stderr, _, _ = await _communicate_bounded(proc, payload, limit)\nexcept BrokenPipeError as e:\n    logger.error(\"hook subprocess pipes unavailable: %s\", e)\n    proc.kill()","preventionTips":["Always create hook subprocesses with stdin/stdout/stderr=PIPE","Don't construct Process objects externally and pass them to the runner","Communicate exactly once per process"],"tags":["brokenpipe","asyncio","subprocess","hooks"],"backgroundTag":"subprocess-pipe-unavailable","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}