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
- Always pair traceBlockBegin/traceBlockEnd (and span event recording) in try/finally so exceptions cannot skip the end call.
- Audit the plugin/interceptor named in the logged PinpointException stack trace and fix its begin/end imbalance.
- Avoid calling trace APIs on a ChildTrace after close0()/close() has run.
- 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
- Always close span events/trace blocks in finally blocks.
- Don't touch a ChildTrace after close(); async continuations need fresh traces.
- When this warning appears, read the attached PinpointException stack trace to identify the offending interceptor/plugin and fix its begin/end pairing.
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
- not found Caused by:
- Duplicated {} scope={}
- bytecode read fail path:{}
- Skip adding interceptor. 'already intercepted method' class=
- Failed to load plugin class {} with classLoader {}
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/ce666c0ac0f8b1c8.
Report an issue: GitHub.