{"record":{"id":"03ae3af22a5ed969","repo":"alibaba/Sentinel","slug":"the-order-of-entry-exit-can-t-be-paired-with-the-o","errorCode":null,"errorMessage":"The order of entry exit can't be paired with the order of entry, current entry in context: <%s>, but expected: <%s>","messagePattern":"The order of entry exit can't be paired with the order of entry, current entry in context: <(.+?)>, but expected: <(.+?)>","errorType":"exception","errorClass":"ErrorEntryFreeException","httpStatus":null,"severity":"error","filePath":"sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java","lineNumber":109,"sourceCode":"        if (context != null) {\n            // Null context should exit without clean-up.\n            if (context instanceof NullContext) {\n                return;\n            }\n\n            if (context.getCurEntry() != this) {\n                String curEntryNameInContext = context.getCurEntry() == null ? null\n                    : context.getCurEntry().getResourceWrapper().getName();\n                // Clean previous call stack.\n                CtEntry e = (CtEntry) context.getCurEntry();\n                while (e != null) {\n                    e.exit(count, args);\n                    e = (CtEntry) e.parent;\n                }\n                String errorMessage = String.format(\"The order of entry exit can't be paired with the order of entry\"\n                        + \", current entry in context: <%s>, but expected: <%s>\", curEntryNameInContext,\n                    resourceWrapper.getName());\n                throw new ErrorEntryFreeException(errorMessage);\n            } else {\n                // Go through the onExit hook of all slots.\n                if (chain != null) {\n                    chain.exit(context, resourceWrapper, count, args);\n                }\n                // Go through the existing terminate handlers (associated to this invocation).\n                callExitHandlersAndCleanUp(context);\n\n                // Restore the call stack.\n                context.setCurEntry(parent);\n                if (parent != null) {\n                    ((CtEntry) parent).child = null;\n                }\n                if (parent == null) {\n                    // Default context (auto entered) will be exited automatically.\n                    if (ContextUtil.isDefaultContext(context)) {\n                        ContextUtil.exit();\n                    }","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/alibaba/Sentinel/blob/a3f40ba8e900c8489bd520274739f17235a7721c/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java#L91-L127","documentation":"CtEntry.exit() requires the context's current entry to be exactly this entry (LIFO unwinding). If another entry sits on top, Sentinel first force-exits the leaked call stack, then throws ErrorEntryFreeException showing the actual current entry name and the expected one. It means SphU.entry/exit calls are unbalanced: an inner entry was never exited before an outer one tried to exit.","triggerScenarios":"Entering resource B inside resource A but skipping B.exit() (e.g. exception path without finally), then calling a.exit(); exiting entries out of order; exiting the same entry twice; exiting entries from a different thread than the one that entered them (Context is thread-local).","commonSituations":"Missing finally around entry.exit(); early returns/continues inside try blocks that skip exit; async code exiting on the wrong thread; batch loops reusing entries incorrectly. Note the side effect: the whole context call stack is cleaned up, so subsequent exits may also fail.","solutions":["Wrap every entry in try-finally: Entry e = SphU.entry(r); try { ... } finally { e.exit(); }","Audit early returns/throws between entry and exit for missing finally","Never share entries across threads; each thread enters/exits its own entries","After catching ErrorEntryFreeException, expect the context to be cleaned — do not reuse stale Entry references"],"exampleFix":"// before\nEntry e = SphU.entry(\"a\");\nEntry inner = SphU.entry(\"b\");\nif (bad) return; // inner never exited\ne.exit();\n\n// after\nEntry e = SphU.entry(\"a\");\ntry {\n    Entry inner = SphU.entry(\"b\");\n    try { if (bad) return; } finally { inner.exit(); }\n} finally {\n    e.exit();\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    entry.exit();\n} catch (ErrorEntryFreeException e) {\n    // call stack was force-cleaned; log and re-initialize context if continuing\n    log.warn(\"Unbalanced entry/exit: {}\", e.getMessage());\n}","preventionTips":["Always use try-finally immediately after SphU.entry so exits cannot be skipped","Never exit entries from a different thread than the one that entered them","Static analysis: search for SphU.entry occurrences not followed by a finally block containing exit()"],"tags":["core","entry-exit","call-stack","resource-leak","error-entry-free"],"backgroundTag":null,"analyzedSha":"a3f40ba8e900c8489bd520274739f17235a7721c","analyzedAt":"2026-08-14T11:10:30.678Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}