{"record":{"id":"20d00196232562e7","repo":"n8n-io/n8n","slug":"failed-to-read-result-from-child-process","errorCode":null,"errorMessage":"Failed to read result from child process","messagePattern":"Failed to read result from child process","errorType":"exception","errorClass":"TaskResultReadError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/task-runner-python/src/task_executor.py","lineNumber":250,"sourceCode":"\n            if process.exitcode == SIGTERM_EXIT_CODE:\n                raise TaskCancelledError()\n\n            if process.exitcode == SIGKILL_EXIT_CODE:\n                raise TaskKilledError()\n\n            if process.exitcode != 0:\n                assert process.exitcode is not None\n                raise TaskSubprocessFailedError(process.exitcode)\n\n            pipe_reader.join(timeout=task_timeout)\n\n            if pipe_reader.is_alive():\n                try:\n                    read_conn.close()\n                except Exception:\n                    pass\n                raise TaskResultReadError(\n                    TimeoutError(f\"Pipe reader timed out after {task_timeout}s\")\n                )\n\n            if pipe_reader.error:\n                raise TaskResultReadError(pipe_reader.error)\n\n            if pipe_reader.pipe_message is None:\n                raise TaskResultMissingError()\n\n            returned = pipe_reader.pipe_message\n\n            if \"error\" in returned:\n                error_msg = cast(PipeErrorMessage, returned)\n                raise TaskRuntimeError(error_msg[\"error\"])\n\n            if \"result\" not in returned:\n                raise TaskResultMissingError()\n","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/task-runner-python/src/task_executor.py#L232-L268","documentation":"TaskResultReadError wraps a TimeoutError raised when pipe_reader.is_alive() is still true after pipe_reader.join(timeout=task_timeout). The child process finished (it was not alive at the earlier join), but the reader thread that drains the result pipe did not finish reading within the same ceiling. The read connection is force-closed before raising.","triggerScenarios":"The child wrote a very large result over the pipe (multi-MB JSON) and the reader thread could not drain it within task_timeout even though the child had exited; or the reader thread is blocked on a partial message because the child was killed mid-write; or the OS pipe buffer is saturated and the reader is slow.","commonSituations":"User code returns a huge array/dataframe that must be serialized and pushed through the pipe; the runner runs on a slow/CPU-throttled node so deserialization lags; an earlier TaskKilledError path left a half-written message that the reader keeps trying to parse.","solutions":["Reduce the size of the returned payload: project/aggregate data in the task so only what n8n needs is sent over the pipe.","Increase task_timeout so the reader has headroom to drain large legitimate payloads after the child exits.","Check for prior kill/cancel events in the same execution that could have left a truncated pipe message; reproduce with a clean run.","Verify the runner host is not CPU-throttled or starved during result serialization."],"exampleFix":"// before\nreturn {'result': huge_dataframe.to_dict('records')}  # MBs over pipe\n// after\nsummary = huge_dataframe[['id','status']].head(1000).to_dict('records')\nreturn {'result': summary}\n","handlingStrategy":"validation","validationCode":"# Estimate result size before returning.\nimport json\ndef safe_return(value, max_bytes):\n    encoded = json.dumps(value, default=str).encode('utf-8')\n    if len(encoded) > max_bytes:\n        return {'result': {'error': 'result too large', 'size': len(encoded)}}\n    return {'result': value}\n","typeGuard":null,"tryCatchPattern":"from n8n_task_runner.errors import TaskResultReadError\ntry:\n    result = TaskExecutor.execute_process(proc, rc, wc, task_timeout, continue_on_fail=False)\nexcept TaskResultReadError as e:\n    # reader could not drain in time; reduce payload or raise timeout\n    return [{\"json\": {\"error\": f\"result read failed: {e}\"}}], [], 0\n","preventionTips":["Cap the size of returned payloads in user code.","Set task_timeout with headroom for serialization of the largest expected result.","Avoid returning deeply nested or recursive structures that serialize slowly.","Monitor pipe read latency to detect drift."],"tags":["python","task-runner","pipe","timeout","serialization"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}