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
- Ensure every created trace is closed even on exception paths (try/finally around the instrumented region)
- Check for leaked traces from earlier requests on the same pooled thread; fix the code that skips trace detachment
- Clear/patch the thread-local binder: upgrade the pinpoint agent if cleanup-on-exception bugs are fixed in newer versions
- 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
- Always close created traces in finally blocks
- Beware pooled threads carrying stale thread-locals between requests
- Fix plugins that start traces without guaranteed detachment
- Check the logged beforeTrace to find the leaking code path
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
- Already closed
- Already closed
- Corrupted call stack found TraceRoot
- TraceBlock already begin. trace=
- absolute start/end time is only supported for TRACE_V3 spans
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)