apache/seatunnel · error · IllegalStateException

Python source process exited with code

Error message

Python source process exited with code {}{}

What it means

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.

Solutions

  1. Read the stderr excerpt in the message for the Python traceback
  2. Fix the unhandled exception or explicit sys.exit in the script
  3. Check container/host logs for OOM kills if the code is 137
  4. Return exit code 0 on successful completion in the script

Example fix

# before
if not rows:
    sys.exit(1)
# after
if not rows:
    sys.exit(0)  # empty input is not an error
Defensive patterns

Strategy: try-catch

Validate before calling

python your_script.py; echo "exit=$?"  # must be 0 on success path

Try / catch

try {
    reader.pollNext(output);
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Python source process exited with code")) {
        // stderr excerpt is embedded; fix script or environment
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/3fc4bed9e8ac50d2. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-connectors-v2/connector-python/src/main/java/org/apache/seatunnel/connectors/seatunnel/python/source/PythonSourceReader.java:510

        }
    }

    /** Returns false when engine cancellation wins the race with normal process completion. */
    private boolean verifyProcessExit() throws Exception {
        int exitCode = process.waitFor();
        if (closeRequested) {
            return false;
        }
        if (!finishPumpsAfterProcessExit()) {
            return false;
        }
        if (closeRequested) {
            return false;
        }
        checkPumpFailures();

        if (exitCode != 0) {
            throw new IllegalStateException(
                    "Python source process exited with code " + exitCode + formatRecentStderr());
        }
        return true;
    }

    /**
     * Waits for stdout EOF after the direct process exits. Buffered rows are returned to pollNext,
     * while an inherited pipe that never reaches EOF fails explicitly instead of hanging forever.
     *
     * @return true when both output pumps are finished and no stdout rows remain buffered
     */
    private boolean finishPumpsAfterProcessExit() throws IOException {
        if (!awaitStdoutPumpAfterProcessExit()) {
            return false;
        }

        IOException joinException = joinThread(stdoutPumpThread, "stdout pump", null);
        if (!stdoutLines.isEmpty()) {

View on GitHub (pinned to cf67b549a7)