alibaba/Sentinel · error · IllegalStateException
Bad async context state, expected entry: %s, but actual: %s
Error message
Bad async context state, expected entry: %s, but actual: %s
What it means
AsyncEntry.exit(true) unwinds the async call stack: it expects the async context's current entry to be this AsyncEntry. If context.getCurEntry() is a different entry (or null), it builds an IllegalStateException naming the expected and actual entries. It means exits are out of order on the async context — e.g. the async entry was exited while inner entries opened on the async context were not yet closed.
Source
Thrown at sentinel-core/src/main/java/com/alibaba/csp/sentinel/AsyncEntry.java:63
void cleanCurrentEntryInLocal() {
if (context instanceof NullContext) {
return;
}
Context originalContext = context;
if (originalContext != null) {
Entry curEntry = originalContext.getCurEntry();
if (curEntry == this) {
Entry parent = this.parent;
originalContext.setCurEntry(parent);
if (parent != null) {
((CtEntry)parent).child = null;
}
} else {
String curEntryName = curEntry == null ? "none"
: curEntry.resourceWrapper.getName() + "@" + curEntry.hashCode();
String msg = String.format("Bad async context state, expected entry: %s, but actual: %s",
getResourceWrapper().getName() + "@" + hashCode(), curEntryName);
throw new IllegalStateException(msg);
}
}
}
public Context getAsyncContext() {
return asyncContext;
}
/**
* The async context should not be initialized until the node for current resource has been set to current entry.
*/
void initAsyncContext() {
if (asyncContext == null) {
if (context instanceof NullContext) {
asyncContext = context;
return;
}
this.asyncContext = Context.newAsyncContext(context.getEntranceNode(), context.getName())View on GitHub (pinned to a3f40ba8e9)
Solutions
- Ensure every SphU.entry inside the async callback has a matching exit in a finally block
- Exit the AsyncEntry only after all inner entries are closed, usually in the async completion callback
- Use try-finally around each entry: try { ... } finally { entry.exit(); }
- Prefer the Reactor adapter (SentinelReactorTransformer) instead of manual asyncEntry for reactive code
Example fix
// before
AsyncEntry e = SphU.asyncEntry("job");
result.onComplete(v -> doWork()); // inner entries never exited
e.exit(); // too early
// after
AsyncEntry e = SphU.asyncEntry("job");
try {
result.onComplete(v -> {
Entry inner = SphU.entry("step");
try { doWork(); } finally { inner.exit(); }
e.exit();
});
} catch (Throwable t) {
e.exit();
throw t;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
asyncEntry.exit();
} catch (IllegalStateException e) {
// stack already unwound elsewhere; log and rebuild context if needed
ContextUtil.exit();
} Prevention
- Pair every inner entry's exit in finally inside async callbacks before exiting the AsyncEntry
- Run async body via ContextUtil.runOnContext(asyncEntry.getAsyncContext(), ...) so entries land on the right context
- For reactive stacks, prefer SentinelReactorTransformer over manual asyncEntry
When it happens
Trigger: Calling asyncEntry.exit() from a different thread/context than where inner resources were entered; exiting the AsyncEntry before exiting entries created inside the async context (e.g. forgetting entry.exit() inside the completion callback); mixing SphU.asyncEntry with synchronous children without pairing exits.
Common situations: Reactor/async callback code that enters resources on the async context but skips exit on error paths; double-exit or exit-after-timeout logic; switching between the original and async context incorrectly via ContextUtil.runOnContext.
Related errors
- The order of entry exit can't be paired with the order of en
- Publisher type is not supported: ${canonicalName}
- ${path}
- The sentinel_default_context can't be permit to defined!
- Invalid maxFileSizeMB
AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14).
Data as JSON: /api/errors/3a5df1cbf95e1ac0.
Report an issue: GitHub.