alibaba/Sentinel · error · ErrorEntryFreeException
The order of entry exit can't be paired with the order of en
Error message
The order of entry exit can't be paired with the order of entry, current entry in context: <%s>, but expected: <%s>
What it means
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.
Source
Thrown at sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java:109
if (context != null) {
// Null context should exit without clean-up.
if (context instanceof NullContext) {
return;
}
if (context.getCurEntry() != this) {
String curEntryNameInContext = context.getCurEntry() == null ? null
: context.getCurEntry().getResourceWrapper().getName();
// Clean previous call stack.
CtEntry e = (CtEntry) context.getCurEntry();
while (e != null) {
e.exit(count, args);
e = (CtEntry) e.parent;
}
String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
+ ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext,
resourceWrapper.getName());
throw new ErrorEntryFreeException(errorMessage);
} else {
// Go through the onExit hook of all slots.
if (chain != null) {
chain.exit(context, resourceWrapper, count, args);
}
// Go through the existing terminate handlers (associated to this invocation).
callExitHandlersAndCleanUp(context);
// Restore the call stack.
context.setCurEntry(parent);
if (parent != null) {
((CtEntry) parent).child = null;
}
if (parent == null) {
// Default context (auto entered) will be exited automatically.
if (ContextUtil.isDefaultContext(context)) {
ContextUtil.exit();
}View on GitHub (pinned to a3f40ba8e9)
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
Example fix
// before
Entry e = SphU.entry("a");
Entry inner = SphU.entry("b");
if (bad) return; // inner never exited
e.exit();
// after
Entry e = SphU.entry("a");
try {
Entry inner = SphU.entry("b");
try { if (bad) return; } finally { inner.exit(); }
} finally {
e.exit();
} Defensive patterns
Strategy: try-catch
Try / catch
try {
entry.exit();
} catch (ErrorEntryFreeException e) {
// call stack was force-cleaned; log and re-initialize context if continuing
log.warn("Unbalanced entry/exit: {}", e.getMessage());
} Prevention
- 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()
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Bad async context state, expected entry: %s, but actual: %s
- The sentinel_default_context can't be permit to defined!
- Invalid maxFileSizeMB
- ${name} is null or empty
AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14).
Data as JSON: /api/errors/03ae3af22a5ed969.
Report an issue: GitHub.