{"record":{"id":"e363fd83e56cf8d9","repo":"pinpoint-apm/pinpoint","slug":"cannot-leave-with-boundary-trace-scope-depth-d","errorCode":null,"errorMessage":"cannot leave with BOUNDARY trace scope. depth: ${depth}","messagePattern":"cannot leave with BOUNDARY trace scope\\. depth: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/scope/BoundaryTraceScope.java","lineNumber":63,"sourceCode":"        }\n    }\n\n    public boolean canLeave() {\n        if (skippedBoundary == 0 && depth == 1) {\n            return true;\n        } else {\n            skippedBoundary--;\n            return false;\n        }\n    }\n\n    public void leave() {\n        if (!isActive()) {\n            throw new IllegalStateException(\"cannot leave with trace scope. depth: \" + depth);\n        }\n\n        if (skippedBoundary != 0 || depth != 1) {\n            throw new IllegalStateException(\"cannot leave with BOUNDARY trace scope. depth: \" + depth);\n        }\n        depth--;\n    }\n\n    @Override\n    public boolean isActive() {\n        return depth > 0;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder sb = new StringBuilder(\"DefaultTraceScope{\");\n        sb.append(\"name='\").append(name).append('\\'');\n        sb.append(\", depth=\").append(depth);\n        sb.append(\", skippedBoundary=\").append(skippedBoundary);\n        sb.append('}');\n        return sb.toString();\n    }","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/scope/BoundaryTraceScope.java#L45-L81","documentation":"BoundaryTraceScope.leave() enforces the internal invariants of a BOUNDARY-scoped trace scope: the scope must be active, no boundary blocks may be skipped (skippedBoundary==0), and the depth must be exactly 1 when leaving. Thrown as IllegalStateException when the code attempts to leave a boundary scope that is unbalanced — i.e. an enter/leave pair mismatch.","triggerScenarios":"Calling leave() when skippedBoundary != 0 (a boundary-skip counter was incremented by skipEnterBoundary without a matching leaveSkipBoundary) or when depth != 1 (multiple nested enters or leave called at the wrong nesting level).","commonSituations":"Custom trace instrumentation plugins that call enterBoundary/leaveBoundary unbalancedly (e.g. missing leave on an exception path), or re-entrancy where the interceptor enters the boundary twice but leaves once.","solutions":["Ensure every enterBoundary() call has a matching leaveBoundary() in a finally block","Check that skipEnterBoundary/leaveSkipBoundary pairs are balanced","Verify the interceptor is not re-entering the same boundary scope before leaving it","Log depth and skippedBoundary at enter/leave points to find the imbalance"],"exampleFix":"// before\ntrace.enterBoundary(1);\ndoWork();\ntrace.leaveBoundary(1);\n// after\ntrace.enterBoundary(1);\ntry {\n    doWork();\n} finally {\n    trace.leaveBoundary(1);\n}","handlingStrategy":"validation","validationCode":"if (!scope.isActive() || scope.getDepth() != 1) { throw new IllegalStateException(\"boundary scope unbalanced before leave\"); }","typeGuard":null,"tryCatchPattern":"try { scope.leave(); } catch (IllegalStateException e) { logger.warn(\"unbalanced boundary scope: {}\", e.getMessage()); }","preventionTips":["Always pair enterBoundary with leaveBoundary in try/finally","Track scope depth manually in complex interceptor chains","Avoid skipping boundaries unless strictly necessary"],"tags":["java","async","tracing","state"],"backgroundTag":"invalid-state-transition","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"}