{"record":{"id":"ce666c0ac0f8b1c8","repo":"pinpoint-apm/pinpoint","slug":"corrupted-call-stack-found-traceroot-callstack","errorCode":null,"errorMessage":"Corrupted call stack found TraceRoot:{}, CallStack:{}","messagePattern":"Corrupted call stack found TraceRoot:(.+?), CallStack:(.+?)","errorType":"console","errorClass":"PinpointException","httpStatus":null,"severity":"warning","filePath":"agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/ChildTrace.java","lineNumber":112,"sourceCode":"        return wrappedSpanEventRecorder(wrappedSpanEventRecorder, spanEvent);\n    }\n\n    private SpanEvent traceBlockBegin0(final int stackId) {\n        if (closed) {\n            if (logger.isWarnEnabled()) {\n                stackDump(\"already closed trace\");\n            }\n            return dummySpanEvent();\n        }\n        // Set properties for the case when stackFrame is not used as part of Span.\n        final SpanEvent spanEvent = newSpanEvent(stackId);\n        this.callStack.push(spanEvent);\n        return spanEvent;\n    }\n\n    private void stackDump(String caused) {\n        PinpointException exception = new PinpointException(caused);\n        logger.warn(\"Corrupted call stack found TraceRoot:{}, CallStack:{}\", getTraceRoot(), callStack, exception);\n    }\n\n    @Override\n    public void traceBlockEnd() {\n        traceBlockEnd(DEFAULT_STACKID);\n    }\n\n    @Override\n    public void traceBlockEnd(int stackId) {\n        if (closed) {\n            if (logger.isWarnEnabled()) {\n                stackDump(\"already closed trace\");\n            }\n            return;\n        }\n\n        final SpanEvent spanEvent = callStack.pop();\n        if (spanEvent == null) {","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/ChildTrace.java#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nrecorder.recordApi(descriptor);\ndoWork(); // may throw -> traceBlockEnd never called\ntrace.traceBlockEnd();\n\n// after\ntrace.traceBlockBegin();\ntry {\n    recorder.recordApi(descriptor);\n    doWork();\n} finally {\n    trace.traceBlockEnd();\n}","handlingStrategy":"try-catch","validationCode":"// maintain your own depth counter around child-trace blocks\nint depth = 0;\n// begin: depth++ ; end: if (depth > 0) { trace.traceBlockEnd(); depth--; }","typeGuard":null,"tryCatchPattern":"trace.traceBlockBegin();\ntry {\n    // span event work\n} catch (Throwable t) {\n    logger.warn(\"work failed; still ending block\", t);\n    throw t;\n} finally {\n    trace.traceBlockEnd(); // guarantees balanced CallStack\n}","preventionTips":["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."],"tags":["call-stack","async-trace","instrumentation","plugin"],"backgroundTag":"internal-invariant-violation","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}