{"record":{"id":"d036525ab36b54d1","repo":"apache/seatunnel","slug":"one-or-more-fetchers-have-encountered-exception","errorCode":null,"errorMessage":"One or more fetchers have encountered exception","messagePattern":"One or more fetchers have encountered exception","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/source/reader/fetcher/SplitFetcherManager.java","lineNumber":145,"sourceCode":"        }\n        return fetchers.isEmpty();\n    }\n\n    public synchronized void close(long timeoutMs) throws Exception {\n        closed = true;\n        fetchers.values().forEach(SplitFetcher::shutdown);\n        executors.shutdown();\n        if (!executors.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS)) {\n            log.warn(\n                    \"Failed to close the source reader in {} ms. There are still {} split fetchers running\",\n                    timeoutMs,\n                    fetchers.size());\n        }\n    }\n\n    public void checkErrors() {\n        if (uncaughtFetcherException.get() != null) {\n            throw new RuntimeException(\n                    \"One or more fetchers have encountered exception\",\n                    uncaughtFetcherException.get());\n        }\n    }\n}\n","sourceCodeStart":127,"sourceCodeEnd":151,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/source/reader/fetcher/SplitFetcherManager.java#L127-L151","documentation":"SplitFetcherManager stores the first uncaught exception from any fetcher thread in an AtomicReference. checkErrors() is polled by the source reader on each record-emission cycle; if a fetcher failed, it rethrows that stored exception wrapped in this RuntimeException so the whole source fails fast.","triggerScenarios":"Any fetcher thread died with an exception (see SplitFetcher.run / FetchTask.run failures), and the main reader thread subsequently calls checkErrors() — the original cause is in the nested stack trace.","commonSituations":"Source connectivity loss (Kafka broker down, JDBC timeout) inside SplitReader; deserialization errors in the fetch path; interruption during shutdown surfacing here after the job already failed elsewhere.","solutions":["Inspect getCause() — this is always a wrapper; the root error is the fetcher's original exception","Fix the underlying source problem identified by the cause (network, credentials, serde)","Add retry/backoff inside the SplitReader for transient source errors so one blip doesn't kill the fetcher","Check earlier logs for the fetcher thread's stack trace, which fires before this aggregate error"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-flight source health check before polling\nif (!fetcherManagerHealthy()) {\n    throw new IllegalStateException(\"source fetchers unhealthy before job start\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    splitFetcherManager.checkErrors();\n} catch (RuntimeException e) {\n    Throwable root = e.getCause();\n    if (root instanceof java.io.IOException && isTransient(root)) {\n        restartFetcher(); // retry path\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always inspect the cause — the real fetcher failure is nested","Implement retry/backoff in SplitReader for transient network/source errors","Monitor fetcher thread health and logs before this aggregate error surfaces","Ensure deserializers are validated against source schema at job start"],"tags":["fetcher","fail-fast","wrapper-exception","source-connector"],"backgroundTag":"upstream-api-error","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"}