{"record":{"id":"919eb0658e1976f5","repo":"apache/seatunnel","slug":"error-sink-worker-thread-is-still-alive-after-clos","errorCode":null,"errorMessage":"Error sink worker thread is still alive after close timeout. jobId={}, pluginName={}, threadName={}. Will release the error sink classloader anyway to reduce classloader leak risk.","messagePattern":"Error sink worker thread is still alive after close timeout\\. jobId=(.+?), pluginName=(.+?), threadName=(.+?)\\. Will release the error sink classloader anyway to reduce classloader leak risk\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/error/DefaultErrorSinkWriter.java","lineNumber":271,"sourceCode":"                throw new RuntimeException(e);\n            }\n        }\n        throwWorkerFailureIfAny();\n    }\n\n    @Override\n    public void close() throws Exception {\n        closed = true;\n        Thread currentWorkerThread = workerThread;\n        if (currentWorkerThread != null) {\n            currentWorkerThread.interrupt();\n        }\n        waitForWorkerTermination(DEFAULT_CLOSE_TIMEOUT_MILLIS);\n\n        Throwable closeEx = null;\n        try {\n            if (currentWorkerThread != null && currentWorkerThread.isAlive()) {\n                log.warn(\n                        \"Error sink worker thread is still alive after close timeout. jobId={}, pluginName={}, threadName={}. \"\n                                + \"Will release the error sink classloader anyway to reduce classloader leak risk.\",\n                        jobId,\n                        sinkConfig.getPluginName(),\n                        currentWorkerThread.getName());\n                closeEx =\n                        new RuntimeException(\n                                String.format(\n                                        \"Timed out waiting for error sink worker to close. jobId=%d, pluginName=%s\",\n                                        jobId, sinkConfig.getPluginName()));\n            } else {\n                // Worker thread stopped; close remaining resources.\n                closeWriterIfPossible();\n            }\n        } catch (Throwable e) {\n            closeEx = e;\n            log.warn(\"Failed to close error sink writer\", e);\n        } finally {","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/error/DefaultErrorSinkWriter.java#L253-L289","documentation":"DefaultErrorSinkWriter.close() waits DEFAULT_CLOSE_TIMEOUT_MILLIS for the error-sink worker thread to finish. If the thread is still alive after the timeout, this warning is logged and the error-sink classloader is released anyway to avoid leaking it. It indicates the error sink could not finish draining within the close timeout, so data may have been dropped.","triggerScenarios":"close() is called while the drain loop still has queued error records and the sink writer (e.g. a slow JDBC/error sink) does not terminate within the close timeout, leaving currentWorkerThread alive.","commonSituations":"Error sink writing to a slow/unresponsive database or network sink during job shutdown; a sink writer blocked in I/O (e.g. MySQL connection stuck); excessive queued error rows at cancel/stop time.","solutions":["Check the error sink (pluginName in the log) for slowness or blocked connections and increase its write throughput or batch size.","Ensure the job is cancelled gracefully so the drain loop can finish before close.","Check for stuck network/DB connections from the sink and tune its timeout options.","If leaks persist, verify plugin classloader cleanup and report/inspect DefaultErrorSinkWriter timeout behavior for your SeaTunnel version."],"exampleFix":"// before: no graceful drain, worker blocked on slow sink\nsinkWriter.write(record); // blocking JDBC call with no timeout\n// after: bound the write so close() can finish in time\nstatement.setQueryTimeout(10);\nsinkWriter.write(record);","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// close() already swallows; monitor logs\ntry {\n    errorSinkWriter.close();\n} catch (Throwable t) {\n    log.error(\"error sink close failed after timeout\", t);\n}","preventionTips":["Keep the error sink fast (timeouts on JDBC statements, batch tuning).","Cancel/stop jobs gracefully rather than hard-kill so the drain loop drains.","Watch logs for this warning as an indicator of slow error sinks.","Size the close timeout for your sink's worst-case latency."],"tags":["classloader-leak","thread-timeout","error-sink","shutdown"],"backgroundTag":"resource-cleanup-timeout","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}