{"record":{"id":"336404aba610eeb9","repo":"apache/seatunnel","slug":"already-an-mdcrunnable","errorCode":null,"errorMessage":"Already an MDCRunnable","messagePattern":"Already an MDCRunnable","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-api/src/main/java/org/apache/seatunnel/api/tracing/MDCTracer.java","lineNumber":66,"sourceCode":" *    logger.info(\"Task is running\");\n *    return null;\n *    }));\n *\n * }</pre>\n */\npublic class MDCTracer {\n\n    public static MDCRunnable tracing(Runnable delegate) {\n        return tracing(MDCContext.current(), delegate);\n    }\n\n    public static MDCRunnable tracing(Long jobId, Runnable delegate) {\n        return tracing(MDCContext.of(jobId), delegate);\n    }\n\n    public static MDCRunnable tracing(MDCContext context, Runnable delegate) {\n        if (delegate instanceof MDCRunnable) {\n            throw new IllegalArgumentException(\"Already an MDCRunnable\");\n        }\n        return new MDCRunnable(context, delegate);\n    }\n\n    public static <V> MDCCallable<V> tracing(Callable<V> delegate) {\n        return tracing(MDCContext.current(), delegate);\n    }\n\n    public static <V> MDCCallable<V> tracing(Long jobId, Callable<V> delegate) {\n        return tracing(MDCContext.of(jobId), delegate);\n    }\n\n    public static <V> MDCCallable<V> tracing(MDCContext context, Callable<V> delegate) {\n        if (delegate instanceof MDCCallable) {\n            throw new IllegalArgumentException(\"Already an MDCCallable\");\n        }\n        return new MDCCallable<>(context, delegate);\n    }","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-api/src/main/java/org/apache/seatunnel/api/tracing/MDCTracer.java#L48-L84","documentation":"MDCTracer.tracing(context, Runnable) rejects a delegate that is already an MDCRunnable. Wrapping twice would apply MDC context twice and restore it incorrectly, so the library fails fast with IllegalArgumentException.","triggerScenarios":"Calling MDCTracer.tracing(jobId, runnable) or tracing(context, runnable) where runnable is already the result of a previous tracing() call, e.g. double-wrapping in shared submission code or a scheduler that wraps tasks twice.","commonSituations":"A task wrapper layer and an executor layer both call MDCTracer.tracing on the same Runnable; refactors that moved tracing to an outer layer without removing the inner call.","solutions":["Pass the raw Runnable to tracing() only once","If the delegate may already be wrapped, check `if (delegate instanceof MDCRunnable)` and use it directly instead of wrapping again","Remove the inner tracing() call at one of the layers"],"exampleFix":"// before\nMDCRunnable r = MDCTracer.tracing(jobId, MDCTracer.tracing(jobId, task));\n// after\nMDCRunnable r = MDCTracer.tracing(jobId, task);","handlingStrategy":"type-guard","validationCode":"MDCRunnable safeWrap(Long jobId, Runnable r) {\n    return r instanceof MDCRunnable ? (MDCRunnable) r : MDCTracer.tracing(jobId, r);\n}","typeGuard":"boolean isWrapped(Runnable r) {\n    return r instanceof MDCRunnable;\n}","tryCatchPattern":"try {\n    return MDCTracer.tracing(jobId, runnable);\n} catch (IllegalArgumentException e) {\n    return (MDCRunnable) runnable;\n}","preventionTips":["Wrap a Runnable with tracing() exactly once, at a single ownership layer","Never re-trace runtimes returned by tracing()","Use the safeWrap helper at API boundaries where provenance is unknown"],"tags":["java","mdc","tracing","double-wrap"],"backgroundTag":"invalid-argument-value","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}