{"record":{"id":"6bfacec1fb9ea6a6","repo":"apache/seatunnel","slug":"python-source-reader-has-already-been-closed","errorCode":null,"errorMessage":"Python source reader has already been closed","messagePattern":"Python source reader has already been closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-python/src/main/java/org/apache/seatunnel/connectors/seatunnel/python/source/PythonSourceReader.java","lineNumber":126,"sourceCode":"    private boolean closeComplete;\n\n    public PythonSourceReader(\n            PythonSourceConfig sourceConfig,\n            CatalogTable catalogTable,\n            SingleSplitReaderContext readerContext) {\n        this.sourceConfig = sourceConfig;\n        this.catalogTable = catalogTable;\n        this.readerContext = readerContext;\n        this.deserializationSchema = createDeserializationSchema(sourceConfig, catalogTable);\n        this.recentStderrLines = new ArrayDeque<>(STDERR_HISTORY_LIMIT);\n        this.stdoutLines = new ArrayBlockingQueue<>(STDOUT_QUEUE_CAPACITY);\n    }\n\n    @Override\n    public void open() throws Exception {\n        synchronized (lifecycleLock) {\n            if (closeRequested) {\n                throw new IOException(\"Python source reader has already been closed\");\n            }\n        }\n        Path scriptPath = validateScriptPath().toAbsolutePath().normalize();\n        Path resolvedExecutable =\n                PythonSourceExecutionPolicy.resolveExecutable(sourceConfig.getPythonExecutable());\n        LOG.warn(\n                \"Python source runs unsandboxed external code. Resolved executable='{}', scriptOrigin='python.script.path={}', guarded by system properties '{}','{}'.\",\n                resolvedExecutable,\n                scriptPath,\n                PythonSourceExecutionPolicy.PYTHON_SOURCE_ENABLED_PROPERTY,\n                PythonSourceExecutionPolicy.PYTHON_ALLOWED_EXECUTABLES_PROPERTY);\n        ProcessBuilder processBuilder =\n                new ProcessBuilder(resolvedExecutable.toString(), scriptPath.toString());\n        configureWorkingDirectory(processBuilder, scriptPath);\n\n        synchronized (lifecycleLock) {\n            if (closeRequested) {\n                throw new IOException(\"Python source reader has already been closed\");","sourceCodeStart":108,"sourceCodeEnd":144,"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#L108-L144","documentation":"Thrown by PythonSourceReader.open() when the reader was already closed (closeRequested flag set) and open() is called afterwards. The lifecycle lock rejects reopening a closed reader because its process handle and buffers are torn down.","triggerScenarios":"Calling open() after close() on the same reader instance; engine/task lifecycle bugs that retry open after a close; test code reusing a reader fixture across cases.","commonSituations":"Custom integration code that restarts a reader by calling open() again; task retry logic recreating state incorrectly; test teardown order issues.","solutions":["Create a new PythonSourceReader instance instead of re-closing/opening the old one","Ensure close() is only called at final teardown, never between open attempts","In retry logic, rebuild the reader from source config rather than reusing it"],"exampleFix":"// before\nreader.close();\nreader.open(); // throws\n// after\nreader.close();\nreader = new PythonSourceReader(...);\nreader.open();","handlingStrategy":"try-catch","validationCode":"if (reader.isClosed()) { reader = createNewReader(config); }","typeGuard":null,"tryCatchPattern":"try {\n    reader.open();\n} catch (java.io.IOException e) {\n    if (e.getMessage().contains(\"already been closed\")) {\n        reader = createNewReader(config);\n        reader.open();\n    } else throw e;\n}","preventionTips":["Treat readers as single-use: one open per instance","Never call open() after close(); build a fresh reader instead","Guard retry logic to recreate the reader"],"tags":["lifecycle","python","state"],"backgroundTag":"invalid-state-transition","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}