apache/shenyu · error · SentinelFallbackException
SentinelFallbackException for non-2xx response status…
Error message
SentinelFallbackException for non-2xx response status (defaults to 500 when status is null)
What it means
The Sentinel plugin registers a watcher consumer on the exchange that throws SentinelFallbackException whenever the final HTTP status is not 2xx (defaulting to 500 for null). The exception drives Sentinel's error accounting and triggers the configured fallback handler via onErrorResume.
Solutions
- Set sentinelHandle's fallbackUri so non-2xx responses are handled by a controlled fallback endpoint.
- Remediate the downstream service causing the non-2xx status.
- Exclude expected 4xx statuses from fallback if business errors should pass through unchanged.
- If the default 500 path fired, investigate why the response status was null when the watcher ran.
Example fix
// before
{"sentinelHandle": {"fallbackUri": ""}}
// after
{"sentinelHandle": {"fallbackUri": "/fallback/sentinel"}} Defensive patterns
Strategy: fallback
Try / catch
return chain.execute(exchange)
.transform(new SentinelReactorTransformer<>(resource))
.onErrorResume(SentinelFallbackException.class, e ->
fallbackHandler.fallback(exchange, UriUtils.createUri(fallbackUri), e)); Prevention
- Set fallbackUri on every sentinel rule that watches HTTP status.
- Alert on downstream non-2xx rates before breakers trip.
- Exclude deliberate 4xx business statuses from triggering fallback where appropriate.
When it happens
Trigger: A request matching a Sentinel resource completes with a non-2xx response status (or null status) — the consumer placed in exchange attributes under WATCHER_HTTP_STATUS throws, and doExecute's onErrorResume routes to fallbackHandler.fallback with the rule's fallbackUri.
Common situations: Downstream API returning 404/500 while a Sentinel rule with fallback is attached; no fallbackUri configured so the client sees the fallback error directly; null status races producing the default 500.
Related errors
- Http StatusCode " + responseEntity.getStatusCode()
- CircuitBreakerStatusCodeException for non-2xx response…
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/ef5b4ff629cddb03.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/main/java/org/apache/shenyu/plugin/sentinel/SentinelPlugin.java:66
private final FallbackHandler fallbackHandler;
public SentinelPlugin(final FallbackHandler fallbackHandler) {
this.fallbackHandler = fallbackHandler;
}
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final ShenyuPluginChain chain, final SelectorData selector, final RuleData rule) {
final ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
Objects.requireNonNull(shenyuContext);
String resourceName = CacheKeyUtils.INST.getKey(rule);
SentinelHandle sentinelHandle = SentinelRuleHandle.CACHED_HANDLE.get().obtainHandle(resourceName);
if (Objects.isNull(sentinelHandle)) {
return chain.execute(exchange);
}
sentinelHandle.checkData();
exchange.getAttributes().put(Constants.WATCHER_HTTP_STATUS, (Consumer<HttpStatus>) status -> {
if (Objects.isNull(status) || !status.is2xxSuccessful()) {
throw new SentinelFallbackException(Objects.isNull(status) ? HttpStatus.INTERNAL_SERVER_ERROR : status);
}
});
return chain.execute(exchange).transform(new SentinelReactorTransformer<>(resourceName)).onErrorResume(throwable ->
fallbackHandler.fallback(exchange, UriUtils.createUri(sentinelHandle.getFallbackUri()), throwable)).doFinally(monoV -> {
final Consumer<HttpStatusCode> consumer = exchange.getAttribute(Constants.METRICS_SENTINEL);
Optional.ofNullable(consumer).ifPresent(c -> c.accept(exchange.getResponse().getStatusCode()));
}
);
}
@Override
public String named() {
return PluginEnum.SENTINEL.getName();
}
@Override
public int getOrder() {
return PluginEnum.SENTINEL.getCode();View on GitHub (pinned to 567142e072)