apache/seatunnel · error · IllegalStateException

MDCContext is not activated

Error message

MDCContext is not activated

What it means

MDCContext tracks whether it was activated (toRestore set to the prior MDC map). deactivate() throws IllegalStateException when called on a context that was never activated via activate(), or that was already deactivated (toRestore nulled afterwards). This guards against unbalanced activate/deactivate pairs corrupting thread-local MDC state.

Source

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

                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;
    }

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

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

        try {
            MDC.remove(JOB_ID);
            MDC.remove(PIPELINE_ID);
            MDC.remove(TASK_ID);
        } catch (Throwable e) {
            log.error("Failed to clear MDC context", e);
            throw e;
        }

        if (this.toRestore != null) {
            this.toRestore.activate();
        }

        return this;
    }

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Ensure activate() is called before deactivate(), e.g. use try-with-resources: context.activate() then try-with-resources on the returned context
  2. Remove the duplicate deactivate()/close() call on the same MDCContext instance
  3. Use MDCContext.of(jobId) only with MDCTracer.tracing(...) wrappers instead of manual activate/deactivate

Example fix

// before
MDCContext ctx = MDCContext.of(jobId);
ctx.deactivate(); // throws: never activated
// after
try (MDCContext ctx = MDCContext.of(jobId).activate()) {
    // job id in MDC
} // close() deactivates exactly once
Defensive patterns

Strategy: try-catch

Validate before calling

// before deactivating
if (ctx != MDCContext.EMPTY && isActivated(ctx)) {
    ctx.deactivate();
}

Type guard

boolean isActivated(MDCContext ctx) {
    return ctx != null && ctx != MDCContext.EMPTY;
}

Try / catch

try {
    ctx.deactivate();
} catch (IllegalStateException e) {
    log.warn("MDCContext deactivate called without matching activate", e);
}

Prevention

When it happens

Trigger: Calling deactivate() on MDCContext.EMPTY (returns this, no throw) is safe, but calling deactivate() on a fresh MDCContext.of(jobId) without a prior activate(), or a second deactivate() after the first already restored and cleared toRestore.

Common situations: Double-closing an MDCContext (close() calls deactivate, so closing twice or closing a context that was never activated); manual lifecycle code where activate() failed or was skipped; passing a non-activated context into try-with-resources.

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/c16aeafe8c0dfcb1. Report an issue: GitHub.