pinpoint-apm/pinpoint · warning
Already closed
Error message
Already closed {} What it means
ChildTrace.close0() logs 'Already closed {}' as a warning when close0() is invoked on a ChildTrace instance whose `closed` flag is already true. ChildTrace is a lightweight per-span trace object, and the library guards against double-close so the underlying TraceRoot is not finalized twice. The warning means some code path closed the trace more than once; the second close is a no-op.
Solutions
- Remove redundant close() calls from application/plugin code and let the agent manage the trace lifecycle
- Guard your own cleanup with an idempotency check (e.g. a local AtomicBoolean) before calling close()
- Ensure each Trace obtained from TraceContext is closed exactly once per request thread
- If seen after upgrading, check the agent version for known double-close bugs in async trace handling
Example fix
// before Trace trace = traceContext.newTraceObject(); doWork(); trace.close(); trace.close(); // warns Already closed // after Trace trace = traceContext.newTraceObject(); doWork(); trace.close();
Defensive patterns
Strategy: try-catch
Validate before calling
// check before cleanup
if (trace != null && !isTraceClosed(trace)) {
trace.close();
} Type guard
boolean isOpen(Trace t) { return t != null && !isClosedFlagSet(t); } Try / catch
try { trace.close(); } catch (RuntimeException e) { logger.debug("trace close skipped", e); } Prevention
- Give one component sole ownership of trace close()
- Use try/finally so close happens exactly once per request
- Never reuse a Trace object after close()
- Log at debug level in your own double-close guards
When it happens
Trigger: Calling close() (which delegates to close0()) twice on the same ChildTrace, e.g. manual close() followed by framework/agent-triggered cleanup, or a finally-block close after the agent already closed the trace when the call stack was force-completed.
Common situations: Plugin/interceptor code that explicitly closes traces plus an agent-side cleanup path both firing; async trace completion racing with synchronous close; reusing a Trace reference after its lifecycle ended.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Already closed
- beforeTrace
- Corrupted call stack found TraceRoot
- TraceBlock already begin. trace=
- absolute start/end time is only supported for TRACE_V3 spans
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/e68a4ad62d32034f.
Report an issue: GitHub.
Appendix: source
Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/ChildTrace.java:177
}
@Override
public boolean isClosed() {
return closed;
}
@Override
public void close() {
if (asyncTraceBlock) {
traceBlockEnd(ASYNC_BEGIN_STACK_ID);
}
close0();
}
public void close0() {
if (closed) {
logger.warn("Already closed {}", this);
return;
}
closed = true;
if (!callStack.empty()) {
if (logger.isWarnEnabled()) {
stackDump("not empty call stack");
}
// skip
} else {
logSpan();
}
this.wrappedSpanEventRecorder.close();
this.storage.close();
}
@Override
public TraceId getTraceId() {View on GitHub (pinned to 744c3d3075)