{"record":{"id":"cd8895a2d7b10b7f","repo":"n8n-io/n8n","slug":"task-subprocess-exited-with-code-exit-code","errorCode":null,"errorMessage":"Task subprocess exited with code {exit_code}","messagePattern":"Task subprocess exited with code (.+?)","errorType":"exception","errorClass":"TaskSubprocessFailedError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/task-runner-python/src/task_executor.py","lineNumber":241,"sourceCode":"                raise TaskSubprocessFailedError(-1, e)\n            finally:\n                write_conn.close()\n\n            process.join(timeout=task_timeout)\n\n            if process.is_alive():\n                TaskExecutor.stop_process(process)\n                raise TaskTimeoutError(task_timeout)\n\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","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/task-runner-python/src/task_executor.py#L223-L259","documentation":"TaskSubprocessFailedError is raised when the child process exited with a non-zero code that is neither SIGTERM nor SIGKILL. The asserted exit_code is propagated so the caller can see exactly how the child died. It is the catch-all for 'the Python task itself crashed' as opposed to being cancelled, killed, or timing out.","triggerScenarios":"User Python code raised an unhandled exception that bubbled to the top level of the child, the child called sys.exit(non_zero), or a C extension aborted the interpreter (segfault manifests as a negative signal-style exit code). The inner try/except around process.start() also rewraps spawn-time failures as TaskSubprocessFailedError(-1, e).","commonSituations":"An exception (NameError, KeyError, ZeroDivisionError, etc.) escapes the user's snippet; sys.exit(1) is called explicitly; a native dependency (e.g. numpy/openssl mismatch) crashes the interpreter; the fork server itself failed to spawn the worker.","solutions":["Wrap the body of user code in try/except and either return a structured error via the result pipe or re-raise with context, so the failure surfaces as a TaskRuntimeError (error key) rather than a non-zero exit.","Reproduce the snippet locally with the same Python and dependency versions to find the unhandled exception; the child's stderr usually has the traceback.","If exit_code is -1 from the spawn path, check that the fork server / Python interpreter / venv used by the runner is healthy and importable.","For native crashes (segfault), align the native library versions (numpy/scipy/openssl) between the runner image and the user's expectations."],"exampleFix":"// before\nresult = do_work()  # raises KeyError, child exits non-zero\n// after\ntry:\n    result = do_work()\nexcept Exception as e:\n    result = {'error': str(e)}\n","handlingStrategy":"try-catch","validationCode":"# Validate the snippet parses before submitting it.\nimport ast\ntry:\n    ast.parse(user_code)\nexcept SyntaxError as e:\n    raise ValueError(f\"refusing to run unparseable task: {e}\")\n","typeGuard":null,"tryCatchPattern":"from n8n_task_runner.errors import TaskSubprocessFailedError\ntry:\n    result = TaskExecutor.execute_process(proc, rc, wc, task_timeout, continue_on_fail=False)\nexcept TaskSubprocessFailedError as e:\n    # e.args[0] is the exit code; capture child stderr separately for the traceback\n    return [{\"json\": {\"error\": f\"subprocess exit {e.args[0]}\", \"detail\": str(e)}}], [], 0\n","preventionTips":["Validate that user code parses (ast.parse) before launching the subprocess.","Have user code catch its own exceptions and return structured errors.","Pin native dependency versions to avoid interpreter crashes.","Capture and log child stderr so non-zero exits are diagnosable."],"tags":["python","task-runner","subprocess","crash","exceptions"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}