{"record":{"id":"3fc4bed9e8ac50d2","repo":"apache/seatunnel","slug":"python-source-process-exited-with-code","errorCode":null,"errorMessage":"Python source process exited with code {}{}","messagePattern":"Python source process exited with code (.+?)(.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-python/src/main/java/org/apache/seatunnel/connectors/seatunnel/python/source/PythonSourceReader.java","lineNumber":510,"sourceCode":"        }\n    }\n\n    /** Returns false when engine cancellation wins the race with normal process completion. */\n    private boolean verifyProcessExit() throws Exception {\n        int exitCode = process.waitFor();\n        if (closeRequested) {\n            return false;\n        }\n        if (!finishPumpsAfterProcessExit()) {\n            return false;\n        }\n        if (closeRequested) {\n            return false;\n        }\n        checkPumpFailures();\n\n        if (exitCode != 0) {\n            throw new IllegalStateException(\n                    \"Python source process exited with code \" + exitCode + formatRecentStderr());\n        }\n        return true;\n    }\n\n    /**\n     * Waits for stdout EOF after the direct process exits. Buffered rows are returned to pollNext,\n     * while an inherited pipe that never reaches EOF fails explicitly instead of hanging forever.\n     *\n     * @return true when both output pumps are finished and no stdout rows remain buffered\n     */\n    private boolean finishPumpsAfterProcessExit() throws IOException {\n        if (!awaitStdoutPumpAfterProcessExit()) {\n            return false;\n        }\n\n        IOException joinException = joinThread(stdoutPumpThread, \"stdout pump\", null);\n        if (!stdoutLines.isEmpty()) {","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-python/src/main/java/org/apache/seatunnel/connectors/seatunnel/python/source/PythonSourceReader.java#L492-L528","documentation":"When the Python child process terminates with a non-zero exit code, verifyProcessExit throws an IllegalStateException including the code and the child's recent stderr. A zero exit means the source finished cleanly; any other code is treated as a task failure.","triggerScenarios":"The Python script finishes (or crashes) with a non-zero exit status — unhandled exception, sys.exit(n), OOM kill, or interpreter error — while pollNext is checking process completion.","commonSituations":"Uncaught exception at the end of a batch script; script calling sys.exit(1) on its own error handling; OOM killer terminating the process (exit code 137); Python version incompatibility causing late failures.","solutions":["Read the stderr excerpt in the message for the Python traceback","Fix the unhandled exception or explicit sys.exit in the script","Check container/host logs for OOM kills if the code is 137","Return exit code 0 on successful completion in the script"],"exampleFix":"# before\nif not rows:\n    sys.exit(1)\n# after\nif not rows:\n    sys.exit(0)  # empty input is not an error","handlingStrategy":"try-catch","validationCode":"python your_script.py; echo \"exit=$?\"  # must be 0 on success path","typeGuard":null,"tryCatchPattern":"try {\n    reader.pollNext(output);\n} catch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"Python source process exited with code\")) {\n        // stderr excerpt is embedded; fix script or environment\n    }\n}","preventionTips":["Wrap script logic in try/except and exit 0 only on success","Handle expected-empty input without sys.exit(1)","Watch for exit 137 (OOM) and size child memory accordingly"],"tags":["process","exit-code","python"],"backgroundTag":"process-exit-nonzero","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}