alibaba/Sentinel · error · IllegalArgumentException
Bad class metadata
Error message
Bad class metadata
What it means
When the netty-http command center writes a response, pickEncoder selects an Encoder from the codec registry for the response body's class. If the response body class is null (handler returned a null-typed result), it throws IllegalArgumentException("Bad class metadata") as a fail-fast guard. The response body class normally comes from CommandResponse.getBody() via getBodyClass reflection.
Source
Thrown at sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandler.java:106
}
private void handleRequest(CommandRequest request, ChannelHandlerContext ctx, boolean keepAlive)
throws Exception {
String commandName = HttpCommandUtils.getTarget(request);
// Find the matching command handler.
CommandHandler<?> commandHandler = getHandler(commandName);
if (commandHandler != null) {
CommandResponse<?> response = commandHandler.handle(request);
writeResponse(response, ctx, keepAlive);
} else {
// No matching command handler.
writeErrorResponse(BAD_REQUEST.code(), String.format("Unknown command \"%s\"", commandName), ctx);
}
}
private Encoder<?> pickEncoder(Class<?> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Bad class metadata");
}
for (Encoder<?> encoder : codecRegistry.getEncoderList()) {
if (encoder.canEncode(clazz)) {
return encoder;
}
}
return null;
}
private void writeErrorResponse(int statusCode, String message, ChannelHandlerContext ctx) {
FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(statusCode),
Unpooled.copiedBuffer(message, Charset.forName(SentinelConfig.charset())));
httpResponse.headers().set("Content-Type", "text/plain; charset=" + SentinelConfig.charset());
ctx.write(httpResponse);
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);View on GitHub (pinned to a3f40ba8e9)
Solutions
- Always return a non-null body from custom CommandHandlers, e.g. CommandResponse.ofSuccess("") or a JSON error string
- Return CommandResponse.ofFailure with a String message instead of null on error paths
- Register a fallback Encoder whose canEncode accepts String and route null bodies to an empty-string response before writeResponse
Example fix
// before
public CommandResponse<String> handle(CommandRequest req) {
String rules = loadRules(req);
return CommandResponse.ofSuccess(rules); // null when nothing loaded
}
// after
public CommandResponse<String> handle(CommandRequest req) {
String rules = loadRules(req);
return CommandResponse.ofSuccess(rules == null ? "[]" : rules);
} Defensive patterns
Strategy: validation
Validate before calling
Object body = response.getBody();
if (body == null) {
response = CommandResponse.ofSuccess(""); // ensure a class exists for encoder pick
} Prevention
- Always return non-null bodies from custom CommandHandlers
- Use CommandResponse.ofFailure("message") for error paths instead of null
When it happens
Trigger: A CommandHandler returning CommandResponse.ofSuccess(null) (or ofFailure with null error body) so the response body is null and its class cannot be determined when HttpServerHandler.writeResponse picks the encoder.
Common situations: Custom CommandHandler implementations that return null bodies for not-found/edge cases; upgrading Sentinel where response encoding became class-based; handlers modeled on ones that always return a String but with a branch returning null.
Related errors
- Bad byte array
- Request cannot be null
- Illegal port: ${port}
- charset is not allowed to be null
- ${name} is null
AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14).
Data as JSON: /api/errors/44f7291dadc4121d.
Report an issue: GitHub.