{"record":{"id":"8b363f308f567880","repo":"apache/flink","slug":"wrong-classifier-chain-circular-chain-of-classifi","errorCode":null,"errorMessage":"Wrong classifier chain; Circular chain of classifiers detected.","messagePattern":"Wrong classifier chain; Circular chain of classifiers detected\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/sink/throwable/FatalExceptionClassifier.java","lineNumber":75,"sourceCode":"    public static FatalExceptionClassifier withRootCauseOfType(\n            Class<? extends Throwable> type, Function<Throwable, Exception> mapper) {\n        return new FatalExceptionClassifier(\n                err -> ExceptionUtils.findThrowable(err, type).isPresent(), mapper);\n    }\n\n    public static FatalExceptionClassifier createChain(FatalExceptionClassifier... classifiers) {\n        Set<FatalExceptionClassifier> importedClassifiers = new HashSet<>();\n\n        if (classifiers.length == 0) {\n            throw new IllegalArgumentException(\"Cannot create empty classifier chain.\");\n        }\n\n        FatalExceptionClassifier tailClassifier = classifiers[0];\n        importedClassifiers.add(tailClassifier);\n\n        for (int i = 1; i < classifiers.length; ++i) {\n            if (importedClassifiers.contains(classifiers[i])) {\n                throw new IllegalArgumentException(\n                        \"Wrong classifier chain; Circular chain of classifiers detected.\");\n            }\n\n            tailClassifier.chainedClassifier = classifiers[i];\n            tailClassifier = classifiers[i];\n            importedClassifiers.add(tailClassifier);\n        }\n\n        return classifiers[0];\n    }\n}\n","sourceCodeStart":57,"sourceCodeEnd":87,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/sink/throwable/FatalExceptionClassifier.java#L57-L87","documentation":"Thrown as an IllegalArgumentException by FatalExceptionClassifier.createChain when the same FatalExceptionClassifier instance appears more than once in the varargs array. createChain builds a singly-linked chain by setting each classifier's chainedClassifier to the next; a duplicate creates a cycle that would cause infinite loops during isFatal traversal. The code detects this using a HashSet of already-imported classifiers.","triggerScenarios":"Passing the same classifier instance twice to createChain, e.g., createChain(classifierA, classifierB, classifierA); or accidentally adding a classifier to a list that already contains it.","commonSituations":"A sink builder programmatically constructs a classifier list and the same instance is added by mistake (aliasing bug); a common base classifier is reused across multiple sink configurations and passed into the same chain.","solutions":["Review the classifier list before calling createChain — ensure each FatalExceptionClassifier instance is unique.","If reusing common classifiers, create distinct instances via withRootCauseOfType rather than sharing references.","Add a deduplication step: new LinkedHashSet<>(Arrays.asList(classifiers)).toArray(...) before createChain."],"exampleFix":"// before — same instance passed twice\nFatalExceptionClassifier chain = FatalExceptionClassifier.createChain(\n    networkErrorClassifier, timeoutClassifier, networkErrorClassifier);\n// after — distinct instances\nFatalExceptionClassifier chain = FatalExceptionClassifier.createChain(\n    FatalExceptionClassifier.withRootCauseOfType(IOException.class, mapper),\n    FatalExceptionClassifier.withRootCauseOfType(TimeoutException.class, mapper));","handlingStrategy":"validation","validationCode":"// Validate uniqueness before calling createChain\nList<FatalExceptionClassifier> deduped = new ArrayList<>(new LinkedHashSet<>(Arrays.asList(classifiers)));\nif (deduped.size() != classifiers.length) {\n    throw new IllegalArgumentException(\"Duplicate classifiers detected in chain\");\n}\nFatalExceptionClassifier.createChain(deduped.toArray(new FatalExceptionClassifier[0]));","typeGuard":null,"tryCatchPattern":"try {\n    FatalExceptionClassifier.createChain(classifiers);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Circular chain\")) {\n        // deduplicate and retry\n        FatalExceptionClassifier[] unique = new LinkedHashSet<>(Arrays.asList(classifiers))\n            .toArray(new FatalExceptionClassifier[0]);\n        FatalExceptionClassifier.createChain(unique);\n    }\n}","preventionTips":["Never reuse the same FatalExceptionClassifier instance in a chain.","Use distinct instances via withRootCauseOfType for each classifier.","Deduplicate the classifier list before calling createChain."],"tags":["async-sink","classifier","cycle-detection","programming-error"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}