{"record":{"id":"44f7291dadc4121d","repo":"alibaba/Sentinel","slug":"bad-class-metadata","errorCode":null,"errorMessage":"Bad class metadata","messagePattern":"Bad class metadata","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandler.java","lineNumber":106,"sourceCode":"    }\n\n    private void handleRequest(CommandRequest request, ChannelHandlerContext ctx, boolean keepAlive)\n        throws Exception {\n        String commandName = HttpCommandUtils.getTarget(request);\n        // Find the matching command handler.\n        CommandHandler<?> commandHandler = getHandler(commandName);\n        if (commandHandler != null) {\n            CommandResponse<?> response = commandHandler.handle(request);\n            writeResponse(response, ctx, keepAlive);\n        } else {\n            // No matching command handler.\n            writeErrorResponse(BAD_REQUEST.code(), String.format(\"Unknown command \\\"%s\\\"\", commandName), ctx);\n        }\n    }\n\n    private Encoder<?> pickEncoder(Class<?> clazz) {\n        if (clazz == null) {\n            throw new IllegalArgumentException(\"Bad class metadata\");\n        }\n        for (Encoder<?> encoder : codecRegistry.getEncoderList()) {\n            if (encoder.canEncode(clazz)) {\n                return encoder;\n            }\n        }\n        return null;\n    }\n\n    private void writeErrorResponse(int statusCode, String message, ChannelHandlerContext ctx) {\n        FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,\n            HttpResponseStatus.valueOf(statusCode),\n            Unpooled.copiedBuffer(message, Charset.forName(SentinelConfig.charset())));\n\n        httpResponse.headers().set(\"Content-Type\", \"text/plain; charset=\" + SentinelConfig.charset());\n        ctx.write(httpResponse);\n\n        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/alibaba/Sentinel/blob/a3f40ba8e900c8489bd520274739f17235a7721c/sentinel-transport/sentinel-transport-netty-http/src/main/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandler.java#L88-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npublic CommandResponse<String> handle(CommandRequest req) {\n    String rules = loadRules(req);\n    return CommandResponse.ofSuccess(rules); // null when nothing loaded\n}\n\n// after\npublic CommandResponse<String> handle(CommandRequest req) {\n    String rules = loadRules(req);\n    return CommandResponse.ofSuccess(rules == null ? \"[]\" : rules);\n}","handlingStrategy":"validation","validationCode":"Object body = response.getBody();\nif (body == null) {\n    response = CommandResponse.ofSuccess(\"\"); // ensure a class exists for encoder pick\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always return non-null bodies from custom CommandHandlers","Use CommandResponse.ofFailure(\"message\") for error paths instead of null"],"tags":["sentinel","transport","netty","null-check","codec"],"backgroundTag":null,"analyzedSha":"a3f40ba8e900c8489bd520274739f17235a7721c","analyzedAt":"2026-08-14T11:10:30.678Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}