{"record":{"id":"5c90110c18d60881","repo":"python/cpython","slug":"cannot-send-input-after-starting-communication","errorCode":null,"errorMessage":"Cannot send input after starting communication","messagePattern":"Cannot send input after starting communication","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/subprocess.py","lineNumber":201,"sourceCode":"            assert fd == 1\n            stream = self.stdout\n            buf = self._stdout_buf\n        if self._loop.get_debug():\n            name = 'stdout' if fd == 1 else 'stderr'\n            logger.debug('%r communicate: read %s', self, name)\n        # Append each block to the persistent buffer as soon as it is\n        # read so that no output is lost if this coroutine is cancelled.\n        while block := await stream.read(stream._limit):\n            buf += block\n        if self._loop.get_debug():\n            name = 'stdout' if fd == 1 else 'stderr'\n            logger.debug('%r communicate: close %s', self, name)\n        transport.close()\n\n    async def communicate(self, input=None):\n        if self._communication_started:\n            if input:\n                raise ValueError(\n                    \"Cannot send input after starting communication\")\n        else:\n            self._input = input\n            self._communication_started = True\n        if self.stdin is not None:\n            stdin = self._feed_stdin(self._input)\n        else:\n            stdin = self._noop()\n        if self.stdout is not None:\n            stdout = self._read_stream(1)\n        else:\n            stdout = self._noop()\n        if self.stderr is not None:\n            stderr = self._read_stream(2)\n        else:\n            stderr = self._noop()\n        await tasks.gather(stdin, stdout, stderr)\n        await self.wait()","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/subprocess.py#L183-L219","documentation":"ValueError raised by asyncio.subprocess.Process.communicate() when it is called a second (or later) time with a non-empty input while _communication_started is already true. Input can only be fed to stdin once, on the first communicate() call; later calls may only reap output with input=None.","triggerScenarios":"await proc.communicate(input=data) followed by another await proc.communicate(input=more) on the same Process object; commonly a retry loop or a wrapper that calls communicate both for setup and teardown of the same process.","commonSituations":"Retry-after-timeout logic that calls communicate() again with the same input on the same (now-exhausted) process; mixed usage where one code path already drained the process with communicate() and another feeds more stdin later; porting subprocess.run-style code that loops.","solutions":["Call communicate(input=...) exactly once per process; re-run the program in a NEW process for retries","For incremental stdin writing, use proc.stdin.write()/drain() instead of communicate","Subsequent output collection on the same process must use communicate() with no input, or read from proc.stdout directly"],"exampleFix":"// before\nout = await proc.communicate(input=payload)\nif retry_needed:\n    out = await proc.communicate(input=payload)  # ValueError\n\n// after\nout = await proc.communicate(input=payload)\nif retry_needed:\n    proc = await asyncio.create_subprocess_exec(*cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)\n    out = await proc.communicate(input=payload)","handlingStrategy":"validation","validationCode":"async def communicate_once(proc, input_=None):\n    if proc.returncode is not None:\n        raise RuntimeError('process already finished; create a new one')\n    return await proc.communicate(input_)","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Call communicate() at most once per Process; re-spawn for retries","Use proc.stdin.write()/drain() for incremental input instead of repeated communicate"],"tags":["asyncio","subprocess","api-misuse","validation"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}