apache/flink · error · IllegalArgumentException
Wrong classifier chain; Circular chain of classifiers detect
Error message
Wrong classifier chain; Circular chain of classifiers detected.
What it means
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.
Source
Thrown at flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/sink/throwable/FatalExceptionClassifier.java:75
public static FatalExceptionClassifier withRootCauseOfType(
Class<? extends Throwable> type, Function<Throwable, Exception> mapper) {
return new FatalExceptionClassifier(
err -> ExceptionUtils.findThrowable(err, type).isPresent(), mapper);
}
public static FatalExceptionClassifier createChain(FatalExceptionClassifier... classifiers) {
Set<FatalExceptionClassifier> importedClassifiers = new HashSet<>();
if (classifiers.length == 0) {
throw new IllegalArgumentException("Cannot create empty classifier chain.");
}
FatalExceptionClassifier tailClassifier = classifiers[0];
importedClassifiers.add(tailClassifier);
for (int i = 1; i < classifiers.length; ++i) {
if (importedClassifiers.contains(classifiers[i])) {
throw new IllegalArgumentException(
"Wrong classifier chain; Circular chain of classifiers detected.");
}
tailClassifier.chainedClassifier = classifiers[i];
tailClassifier = classifiers[i];
importedClassifiers.add(tailClassifier);
}
return classifiers[0];
}
}
View on GitHub (pinned to 2f3c205e92)
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.
Example fix
// before — same instance passed twice
FatalExceptionClassifier chain = FatalExceptionClassifier.createChain(
networkErrorClassifier, timeoutClassifier, networkErrorClassifier);
// after — distinct instances
FatalExceptionClassifier chain = FatalExceptionClassifier.createChain(
FatalExceptionClassifier.withRootCauseOfType(IOException.class, mapper),
FatalExceptionClassifier.withRootCauseOfType(TimeoutException.class, mapper)); Defensive patterns
Strategy: validation
Validate before calling
// Validate uniqueness before calling createChain
List<FatalExceptionClassifier> deduped = new ArrayList<>(new LinkedHashSet<>(Arrays.asList(classifiers)));
if (deduped.size() != classifiers.length) {
throw new IllegalArgumentException("Duplicate classifiers detected in chain");
}
FatalExceptionClassifier.createChain(deduped.toArray(new FatalExceptionClassifier[0])); Try / catch
try {
FatalExceptionClassifier.createChain(classifiers);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Circular chain")) {
// deduplicate and retry
FatalExceptionClassifier[] unique = new LinkedHashSet<>(Arrays.asList(classifiers))
.toArray(new FatalExceptionClassifier[0]);
FatalExceptionClassifier.createChain(unique);
}
} Prevention
- Never reuse the same FatalExceptionClassifier instance in a chain.
- Use distinct instances via withRootCauseOfType for each classifier.
- Deduplicate the classifier list before calling createChain.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot create empty classifier chain.
- Please override the method.
- The request entry sent to the buffer was of size [%s], when
- Corrupted data to deserialize
- Invalid option %s. Must be a positive integer.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/8b363f308f567880.
Report an issue: GitHub.