apache/seatunnel · error · IllegalStateException

MDCContext is already activated

Error message

MDCContext is already activated

What it means

MDCContext.activate() installs job-id context into the SLF4J MDC and stores the previous context in toRestore. Activating the same MDCContext instance twice without closing it would overwrite toRestore and leak MDC state, so a second activate() on an already-active context throws IllegalStateException. EMPTY contexts are idempotent and exempt.

Source

Thrown at seatunnel-api/src/main/java/org/apache/seatunnel/api/tracing/MDCContext.java:76

    private final Long jobId;
    private final Long pipelineId;
    private final Long taskId;
    private transient volatile MDCContext toRestore;

    public MDCContext(Long jobId, Long pipelineId, Long taskId) {
        this.jobId = jobId;
        this.pipelineId = pipelineId;
        this.taskId = taskId;
    }

    public synchronized MDCContext activate() {
        if (this == EMPTY) {
            return this;
        }

        if (this.toRestore != null) {
            throw new IllegalStateException("MDCContext is already activated");
        }
        this.toRestore = current();

        try {
            if (jobId != null) {
                MDC.put(JOB_ID, String.valueOf(jobId));
            }
            if (pipelineId != null) {
                MDC.put(PIPELINE_ID, String.valueOf(pipelineId));
            }
            if (taskId != null) {
                MDC.put(TASK_ID, String.valueOf(taskId));
            }
        } catch (Throwable e) {
            log.error("Failed to put MDC context", e);
            throw e;
        }
        return this;

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Activate each MDCContext exactly once, in a try-with-resources block
  2. Use separate MDCContext instances for nested scopes instead of reusing one
  3. Check whether an outer layer already activates the context (interceptors/filters) and remove the duplicate call
  4. If you need re-entrancy, track activation yourself or use the context factory to create a fresh instance per scope

Example fix

// before
MDCContext ctx = new MDCContext(jobId);
try (MDCContext a = ctx.activate()) {
    try (MDCContext b = ctx.activate()) { // throws: already activated
// after
MDCContext ctx = new MDCContext(jobId);
try (MDCContext a = ctx.activate()) {
    runInner(); // inner code uses ambient MDC, no second activate()
}
Defensive patterns

Strategy: try-catch

Validate before calling

// detect double activation before it throws:
// structure code so each MDCContext instance is activated once
try (MDCContext ctx = new MDCContext(jobId).activate()) {
    // scope
}

Try / catch

try {
    mdcContext.activate();
} catch (IllegalStateException e) {
    // context already active in this scope — proceed without re-activating
}

Prevention

When it happens

Trigger: Calling activate() twice on the same non-EMPTY MDCContext instance without close()/restore in between, e.g. wrapping a scope twice or activating in nested try-with-resources blocks sharing one instance.

Common situations: Tracing/observability code that opens the same context scope in both an outer wrapper and inner interceptor; accidental double-activation in job submission paths; reusing a cached MDCContext across sequential code blocks without deactivating.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/9af4bc7e64072e3d. Report an issue: GitHub.