apache/seatunnel · error · IllegalArgumentException
Already an MDCCallable
Error message
Already an MDCCallable
What it means
MDCTracer.tracing(context, Callable) rejects a delegate already wrapped as MDCCallable via IllegalArgumentException. It prevents nested MDC wrapping of Callables, which would snapshot/restore the wrong MDC state on completion.
Source
Thrown at seatunnel-api/src/main/java/org/apache/seatunnel/api/tracing/MDCTracer.java:81
public static MDCRunnable tracing(MDCContext context, Runnable delegate) {
if (delegate instanceof MDCRunnable) {
throw new IllegalArgumentException("Already an MDCRunnable");
}
return new MDCRunnable(context, delegate);
}
public static <V> MDCCallable<V> tracing(Callable<V> delegate) {
return tracing(MDCContext.current(), delegate);
}
public static <V> MDCCallable<V> tracing(Long jobId, Callable<V> delegate) {
return tracing(MDCContext.of(jobId), delegate);
}
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);
}View on GitHub (pinned to cf67b549a7)
Solutions
- Trace the Callable only once, at the outermost layer
- Guard with `if (delegate instanceof MDCCallable) use it else tracing(...)`
- Strip the earlier wrap and pass the unwrapped Callable
Example fix
// before MDCCallable<V> c = MDCTracer.tracing(jobId, MDCTracer.tracing(jobId, call)); // after MDCCallable<V> c = MDCTracer.tracing(jobId, call);
Defensive patterns
Strategy: type-guard
Validate before calling
MDCCallable<V> safeWrap(Long jobId, Callable<V> c) {
return c instanceof MDCCallable ? (MDCCallable<V>) c : MDCTracer.tracing(jobId, c);
} Type guard
boolean isWrapped(Callable<?> c) {
return c instanceof MDCCallable;
} Try / catch
try {
return MDCTracer.tracing(jobId, callable);
} catch (IllegalArgumentException e) {
return (MDCCallable<V>) callable;
} Prevention
- Trace Callables at one layer only
- Do not re-wrap cached or previously submitted traced Callables
- Check instanceof MDCCallable before wrapping at boundaries
When it happens
Trigger: Calling tracing(jobId, callable) or tracing(context, callable) where callable is already an MDCCallable from a previous tracing() call.
Common situations: Double-wrapping tasks submitted through both a helper utility and the executor wrapper; caching already-traced Callables and re-tracing them.
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 MDCExecutor
- Already an MDCConsumer
- Already an MDCFunction
- Already an MDCPredicate
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/d7b8b0927127fb89.
Report an issue: GitHub.