apache/seatunnel · error · IllegalArgumentException
Already an MDCExecutor
Error message
Already an MDCExecutor
What it means
MDCTracer.tracing(context, Executor) rejects a delegate that is already an MDCExecutor with IllegalArgumentException. Double-wrapping an Executor would propagate MDC context through two layers and restore state out of order.
Source
Thrown at seatunnel-api/src/main/java/org/apache/seatunnel/api/tracing/MDCTracer.java:96
public static <V> MDCCallable<V> tracing(MDCContext context, Callable<V> delegate) {
if (delegate instanceof MDCCallable) {
throw new IllegalArgumentException("Already an MDCCallable");
}
return new MDCCallable<>(context, delegate);
}
public static MDCExecutor tracing(Executor delegate) {
return tracing(MDCContext.current(), delegate);
}
public static MDCExecutor tracing(Long jobId, Executor delegate) {
return tracing(MDCContext.of(jobId), delegate);
}
public static MDCExecutor tracing(MDCContext context, Executor delegate) {
if (delegate instanceof MDCExecutor) {
throw new IllegalArgumentException("Already an MDCExecutor");
}
return new MDCExecutor(context, delegate);
}
public static MDCExecutorService tracing(ExecutorService delegate) {
return tracing(MDCContext.current(), delegate);
}
public static MDCExecutorService tracing(Long jobId, ExecutorService delegate) {
return tracing(MDCContext.of(jobId), delegate);
}
public static MDCExecutorService tracing(MDCContext context, ExecutorService delegate) {
if (delegate instanceof MDCExecutor) {
throw new IllegalArgumentException("Already an MDCExecutor");
}
return new MDCExecutorService(context, delegate);
}View on GitHub (pinned to cf67b549a7)
Solutions
- Reuse the existing MDCExecutor instead of wrapping again
- Keep a single wrapping point (executor factory) and never re-trace returned executors
- Guard with `if (executor instanceof MDCExecutor) use it else tracing(...)`
Example fix
// before MDCExecutor e = MDCTracer.tracing(jobId, MDCTracer.tracing(jobId, pool)); // after MDCExecutor e = MDCTracer.tracing(jobId, pool);
Defensive patterns
Strategy: type-guard
Validate before calling
Executor safeWrap(Long jobId, Executor e) {
return e instanceof MDCExecutor ? e : MDCTracer.tracing(jobId, e);
} Type guard
boolean isWrapped(Executor e) {
return e instanceof MDCExecutor;
} Try / catch
try {
return MDCTracer.tracing(jobId, executor);
} catch (IllegalArgumentException e) {
return (MDCExecutor) executor;
} Prevention
- Wrap executors once in a central factory
- Never re-trace executors returned by another module
- Remember MDCExecutorService/MDCScheduledExecutorService also satisfy the instanceof check
When it happens
Trigger: Calling tracing(jobId, executor) or tracing(context, executor) where executor is already an MDCExecutor (possibly from wrapping an ExecutorService, since MDCExecutorService subclasses MDCExecutor).
Common situations: An application already returns MDC-wrapped executors from a factory and calling code wraps again; wrapping an MDCExecutorService obtained from tracing(ExecutorService).
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 MDCConsumer
- Already an MDCFunction
- Already an MDCPredicate
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/d11e5728365360cc.
Report an issue: GitHub.