pinpoint-apm/pinpoint · warning · PinpointException

Corrupted call stack found TraceRoot:{}, CallStack:{}

Error message

Corrupted call stack found TraceRoot:{}, CallStack:{}

What it means

WARN from ChildTrace.stackDump() logging a corrupted internal call stack. ChildTrace keeps a CallStack of span events; when operations like traceBlockEnd() or close0() observe an unbalanced stack (e.g. popping on empty or mismatched depth), it constructs a PinpointException purely to capture a stack trace and logs the TraceRoot and CallStack contents. The trace data produced by this ChildTrace is unreliable from that point on.

Source

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

        return wrappedSpanEventRecorder(wrappedSpanEventRecorder, spanEvent);
    }

    private SpanEvent traceBlockBegin0(final int stackId) {
        if (closed) {
            if (logger.isWarnEnabled()) {
                stackDump("already closed trace");
            }
            return dummySpanEvent();
        }
        // Set properties for the case when stackFrame is not used as part of Span.
        final SpanEvent spanEvent = newSpanEvent(stackId);
        this.callStack.push(spanEvent);
        return spanEvent;
    }

    private void stackDump(String caused) {
        PinpointException exception = new PinpointException(caused);
        logger.warn("Corrupted call stack found TraceRoot:{}, CallStack:{}", getTraceRoot(), callStack, exception);
    }

    @Override
    public void traceBlockEnd() {
        traceBlockEnd(DEFAULT_STACKID);
    }

    @Override
    public void traceBlockEnd(int stackId) {
        if (closed) {
            if (logger.isWarnEnabled()) {
                stackDump("already closed trace");
            }
            return;
        }

        final SpanEvent spanEvent = callStack.pop();
        if (spanEvent == null) {

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Always pair traceBlockBegin/traceBlockEnd (and span event recording) in try/finally so exceptions cannot skip the end call.
  2. Audit the plugin/interceptor named in the logged PinpointException stack trace and fix its begin/end imbalance.
  3. Avoid calling trace APIs on a ChildTrace after close0()/close() has run.
  4. Reproduce locally with the logged CallStack dump and count pushes vs pops to find the leak.

Example fix

// before
recorder.recordApi(descriptor);
doWork(); // may throw -> traceBlockEnd never called
trace.traceBlockEnd();

// after
trace.traceBlockBegin();
try {
    recorder.recordApi(descriptor);
    doWork();
} finally {
    trace.traceBlockEnd();
}
Defensive patterns

Strategy: try-catch

Validate before calling

// maintain your own depth counter around child-trace blocks
int depth = 0;
// begin: depth++ ; end: if (depth > 0) { trace.traceBlockEnd(); depth--; }

Try / catch

trace.traceBlockBegin();
try {
    // span event work
} catch (Throwable t) {
    logger.warn("work failed; still ending block", t);
    throw t;
} finally {
    trace.traceBlockEnd(); // guarantees balanced CallStack
}

Prevention

When it happens

Trigger: Unbalanced traceBlockBegin/traceBlockEnd pairs on a child (async) trace; an exception path that skips traceBlockEnd; calling traceBlockEnd() more times than begin() or after the trace was closed.

Common situations: Custom plugin code that begins a span event in a try block but ends it only on the happy path without finally; async callbacks completing after the parent trace closed; interceptor exceptions leaving the stack unbalanced.

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