apache/seatunnel · error · IllegalArgumentException
Already an MDCConsumer
Error message
Already an MDCConsumer
What it means
MDCTracer.tracing(context, Consumer) rejects a delegate already wrapped as MDCConsumer with IllegalArgumentException. Double wrapping would capture and restore MDC state twice around a single consumer invocation.
Source
Thrown at seatunnel-api/src/main/java/org/apache/seatunnel/api/tracing/MDCTracer.java:143
public static MDCScheduledExecutorService tracing(
MDCContext context, ScheduledExecutorService delegate) {
if (delegate instanceof MDCExecutor) {
throw new IllegalArgumentException("Already an MDCExecutor");
}
return new MDCScheduledExecutorService(context, delegate);
}
public static <T> MDCConsumer<T> tracing(Consumer<T> delegate) {
return tracing(MDCContext.current(), delegate);
}
public static <T> MDCConsumer<T> tracing(Long jobId, Consumer<T> delegate) {
return tracing(MDCContext.of(jobId), delegate);
}
public static <T> MDCConsumer<T> tracing(MDCContext context, Consumer<T> delegate) {
if (delegate instanceof MDCConsumer) {
throw new IllegalArgumentException("Already an MDCConsumer");
}
return new MDCConsumer<>(context, delegate);
}
public static <T, R> MDCFunction<T, R> tracing(Function<T, R> delegate) {
return tracing(MDCContext.current(), delegate);
}
public static <T, R> MDCFunction<T, R> tracing(Long jobId, Function<T, R> delegate) {
return tracing(MDCContext.of(jobId), delegate);
}
public static <T, R> MDCFunction<T, R> tracing(MDCContext context, Function<T, R> delegate) {
if (delegate instanceof MDCFunction) {
throw new IllegalArgumentException("Already an MDCFunction");
}
return new MDCFunction<>(context, delegate);
}View on GitHub (pinned to cf67b549a7)
Solutions
- Apply tracing at one layer only and pass the MDCConsumer down as-is
- Guard: `if (c instanceof MDCConsumer) use it else tracing(...)`
- Remove the redundant tracing call in the inner layer
Example fix
// before MDCConsumer<T> c = MDCTracer.tracing(jobId, MDCTracer.tracing(jobId, consumer)); // after MDCConsumer<T> c = MDCTracer.tracing(jobId, consumer);
Defensive patterns
Strategy: type-guard
Validate before calling
MDCConsumer<T> safeWrap(Long jobId, Consumer<T> c) {
return c instanceof MDCConsumer ? (MDCConsumer<T>) c : MDCTracer.tracing(jobId, c);
} Type guard
boolean isWrapped(Consumer<?> c) {
return c instanceof MDCConsumer;
} Try / catch
try {
return MDCTracer.tracing(jobId, consumer);
} catch (IllegalArgumentException e) {
return (MDCConsumer<T>) consumer;
} Prevention
- Wrap consumers at one pipeline layer only
- Do not re-trace consumers handed to you by other components
- Add an instanceof MDCConsumer pass-through in wrapper utilities
When it happens
Trigger: Calling tracing(jobId, consumer) or tracing(context, consumer) where consumer is already an MDCConsumer produced by an earlier tracing() call, e.g. wrapping sink record consumers twice.
Common situations: Sink/committer pipelines where both a generic wrapper and a connector-specific wrapper apply tracing to the same Consumer.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Already an MDCRunnable
- Already an MDCCallable
- Already an MDCExecutor
- Already an MDCFunction
- Already an MDCPredicate
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/d482b89c946e50b5.
Report an issue: GitHub.