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

  1. Ensure each async trace binds to a unique, unbound Reference
  2. Check for double invocation of newAsyncContextTrace on the same context
  3. Review async trace close/complete lifecycle so references are cleared before reuse
  4. 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

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


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