pinpoint-apm/pinpoint · error · PinpointException

beforeTrace

Error message

beforeTrace:{}

What it means

DefaultTraceFactory.checkAndGet() logs 'beforeTrace:{}' (with the stale Trace and a PinpointException 'already Trace Object exist.') and then throws when the thread-local trace reference already holds a non-null Trace before binding a new one. The library enforces at most one active trace per thread; this error means a previous trace was never removed from the thread-local slot.

Solutions

  1. Ensure every created trace is closed even on exception paths (try/finally around the instrumented region)
  2. Check for leaked traces from earlier requests on the same pooled thread; fix the code that skips trace detachment
  3. Clear/patch the thread-local binder: upgrade the pinpoint agent if cleanup-on-exception bugs are fixed in newer versions
  4. Inspect the logged 'beforeTrace' object to identify which code path created the stale trace

Example fix

// before
Trace trace = traceContext.newTraceObject();
doRiskyWork(); // throws -> trace never closed, thread-local stays bound
trace.close();
// after
Trace trace = traceContext.newTraceObject();
try {
    doRiskyWork();
} finally {
    trace.close(); // detaches from thread-local
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before creating a new trace, verify the thread-local is free
if (traceContext.currentTraceObject() != null) { logger.warn("stale trace on thread"); }

Type guard

boolean threadTraceFree(TraceContext ctx) { return ctx.currentTraceObject() == null; }

Try / catch

try { trace = traceContext.newTraceObject(); } catch (PinpointException e) { logger.warn("thread-local trace already bound", e); }

Prevention

When it happens

Trigger: Calling newTraceObject/currentTraceObject creation while a prior Trace for the same thread was never closed or detached; an exception path that skipped the trace cleanup; interceptor chains starting a new trace without releasing the old one.

Common situations: Exceptions in instrumented code that bypass the end-side interceptor which normally nulls the thread-local; leaked traces from previous requests on pooled/reused threads (servlet containers, thread pools); plugin bugs failing to close traces.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/4a7554f8c15d070a. Report an issue: GitHub.

Appendix: source

Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/DefaultTraceFactory.java:105

        return trace;
    }


    @Override
    public Trace continueTraceObject(Trace trace) {
        final Reference<Trace> reference = checkAndGet();

        bind(reference, trace);
        return trace;
    }

    private Reference<Trace> checkAndGet() {
        final Reference<Trace> reference = this.threadLocalBinder.get();
        final Trace old = reference.get();
        if (old != null) {
            final PinpointException exception = new PinpointException("already Trace Object exist.");
            if (logger.isWarnEnabled()) {
                logger.warn("beforeTrace:{}", old, exception);
            }
            throw exception;
        }
        return reference;
    }

    @Override
    public Trace newTraceObject() {
        final Reference<Trace> reference = checkAndGet();
        final Trace trace = this.baseTraceFactory.newTraceObject();

        bind(reference, trace);
        return trace;
    }

    @Override
    public Trace newTraceObject(String urlPath) {
        final Reference<Trace> reference = checkAndGet();

View on GitHub (pinned to 744c3d3075)