pinpoint-apm/pinpoint · error · IllegalStateException
traceReference is null
Error message
traceReference is null
What it means
DefaultAsyncContext.bind() throws IllegalStateException('traceReference is null') when the reference passed in already holds a non-null trace (despite the misleading message) before binding an async trace. It protects the invariant that a fresh async trace slot must be empty before binding.
Source
Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/DefaultAsyncContext.java:135
}
final Reference<Trace> reference = remote.binder().get();
final Trace nestedTrace = reference.get();
if (nestedTrace != null) {
// same nesting semantics as continueAsyncTraceObject(boolean)
if (nestedTrace.canSampled()) {
return nestedTrace;
}
return null;
}
// rebind the caller's previously-created trace: no new LocalAsyncId, ChildTrace,
// recorders or scope registration - just the thread binding.
reference.set(reuse);
return reuse;
}
private void bind(Reference<Trace> reference, Trace asyncTrace) {
if (reference.get() != null) {
throw new IllegalStateException("traceReference is null");
}
reference.set(asyncTrace);
}
@Override
public Trace currentAsyncTraceObject() {
final Reference<Trace> reference = remote.binder().get();
final Trace trace = reference.get();
if (trace == null) {
return null;
}
if (trace.canSampled()) {
return trace;
}
return null;
}View on GitHub (pinned to 744c3d3075)
Solutions
- Ensure each async trace binds to a unique, unbound Reference
- Check for double invocation of newAsyncContextTrace on the same context
- Review async trace close/complete lifecycle so references are cleared before reuse
- Upgrade pinpoint to a version with the fixed async context lifecycle
Example fix
// before: binding twice asyncContext.bind(reference, trace1); asyncContext.bind(reference, trace2); // throws // after: close first trace or obtain a fresh reference trace1.close(); asyncContext.bind(newReference, trace2);
Defensive patterns
Strategy: try-catch
Validate before calling
if (reference.get() != null) {
// slot already bound; create a fresh reference instead of binding
} Type guard
boolean isBindSafe(Reference<Trace> ref) {
return ref.get() == null;
} Try / catch
try {
asyncContext.bind(reference, asyncTrace);
} catch (IllegalStateException e) {
logger.warn("async trace slot already bound", e);
// close old trace or allocate a new reference
} Prevention
- Bind each async trace to a unique Reference
- Close traces promptly so references are released
- Avoid double-invoking newAsyncContextTrace for the same context
- Upgrade Pinpoint where async lifecycle races are fixed
When it happens
Trigger: Calling newAsyncContextTrace/currentAsyncTraceObject paths that reach bind(reference, asyncTrace) while reference.get() != null — i.e. binding an async trace into a slot that already contains one.
Common situations: Async trace lifecycle races where the same reference is bound twice; reusing an AsyncContext after its trace already started; concurrency bugs in collector/plugin async handling.
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
- traceReference is null
- UNSUPPORTED_OPERATION
- startTime not recorded
- cannot leave with BOUNDARY trace scope. depth: ${depth}
- Cannot leave with scope. depth: ${depth}
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/4027bd06b79fff83.
Report an issue: GitHub.