alibaba/Sentinel · error · SentinelClusterException

UNEXPECTED_STATUS

UNEXPECTED_STATUS

Error message

unexpected status

What it means

After a successful promise.await in sendRequest(), the code looks up the completed entry by xid in TokenClientPromiseHolder. If the entry or its response value is null, it throws SentinelClusterException(UNEXPECTED_STATUS) with the source comment 'Should not go through here'. It fires when the promise completed (usually exceptionally, e.g. channel closed) without a response ever being stored — a rare internal inconsistency path.

Source

Thrown at sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/NettyTransportClient.java:231

            throw new SentinelClusterException(ClusterErrorMessages.BAD_REQUEST);
        }
        int xid = getCurrentId();
        try {
            request.setId(xid);

            channel.writeAndFlush(request);

            ChannelPromise promise = channel.newPromise();
            TokenClientPromiseHolder.putPromise(xid, promise);

            if (!promise.await(ClusterClientConfigManager.getRequestTimeout())) {
                throw new SentinelClusterException(ClusterErrorMessages.REQUEST_TIME_OUT);
            }

            SimpleEntry<ChannelPromise, ClusterResponse> entry = TokenClientPromiseHolder.getEntry(xid);
            if (entry == null || entry.getValue() == null) {
                // Should not go through here.
                throw new SentinelClusterException(ClusterErrorMessages.UNEXPECTED_STATUS);
            }
            return entry.getValue();
        } finally {
            TokenClientPromiseHolder.remove(xid);
        }
    }

    private int getCurrentId() {
        int pre, next;
        do {
            pre = idGenerator.get();
            next = pre >= MAX_ID ? MIN_ID : pre + 1;
        } while (!idGenerator.compareAndSet(pre, next));
        return next;
    }

    /*public CompletableFuture<ClusterResponse> sendRequestAsync(ClusterRequest request) {
        // Uncomment this when min target JDK is 1.8.

View on GitHub (pinned to a3f40ba8e9)

Solutions

  1. Treat like other cluster failures: catch SentinelClusterException and degrade to local flow control
  2. Check client-server connectivity and TokenClientHandler logs for the underlying channel error
  3. Upgrade Sentinel — later versions hardened promise failure handling in NettyTransportClient/TokenClientPromiseHolder
  4. Ensure only one event loop touches the promise holder (no custom client modifications)

Example fix

// before
TokenResult r = tokenService.requestToken(name, count, prioritized);

// after
TokenResult r;
try {
    r = tokenService.requestToken(name, count, prioritized);
} catch (SentinelClusterException e) {
    logger.warn("cluster request failed: {}", e.getMessage());
    r = new TokenResult(TokenResultStatus.FAIL); // local fallback decision
}
Defensive patterns

Strategy: fallback

Try / catch

try {
    result = tokenService.requestToken(name, count, prioritized);
} catch (SentinelClusterException e) {
    logger.warn("cluster transport anomaly: {}", e.getMessage());
    result = localModeRequest(name, count);
}

Prevention

When it happens

Trigger: The channel fails/closes after writeAndFlush but before the response arrives — the promise is failed (completing the await) while TokenClientHandler never put a ClusterResponse for that xid; concurrent disconnect racing an in-flight request.

Common situations: Token server or connection dropping mid-request; Netty pipeline exceptions; seen intermittently during token-server restarts or network flaps.

Related errors


AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14). Data as JSON: /api/errors/c37f45106a0749cd. Report an issue: GitHub.