apache/druid · error · QueryDriver.RequestError
The class [%s] is not a Protobuf
Error message
The class [%s] is not a Protobuf
What it means
In getProtobufClass, Class.forName succeeds but the cast to Class<GeneratedMessage> fails (the loaded class is not a protobuf message type), throwing a ClassCastException that is wrapped in RequestError("The class [%s] is not a Protobuf"). The named class exists but is not a GeneratedMessage, so it cannot be used to encode result rows.
Source
Thrown at extensions-contrib/grpc-query/src/main/java/org/apache/druid/grpc/server/QueryDriver.java:764
GrpcResultsAccumulator accumulator = new GrpcResultsAccumulator(writer);
accumulator.push(result);
return ByteString.copyFrom(out.toByteArray());
}
@SuppressWarnings("unchecked")
private Class<GeneratedMessage> getProtobufClass(final QueryRequest request)
{
try {
return (Class<GeneratedMessage>) Class.forName(request.getProtobufMessageName());
}
catch (ClassNotFoundException e) {
throw new RequestError(
"The Protobuf class [%s] is not known. Is your protobuf jar on the class path?",
request.getProtobufMessageName()
);
}
catch (ClassCastException e) {
throw new RequestError(
"The class [%s] is not a Protobuf",
request.getProtobufMessageName()
);
}
}
}
View on GitHub (pinned to 9b90983fd2)
Solutions
- Use the exact generated message class name (extends GeneratedMessageV3/GeneratedMessage), not the outer file class or Builder.
- Verify with Class.forName + GeneratedMessage.class.isAssignableFrom in a test before sending the request.
- Regenerate the protobuf classes with the protobuf Java compiler so the named class is a real message type.
- Check that the deployed jar is the compiled .proto output, not hand-written POJOs of the same names.
Example fix
// before
request.setProtobufMessageName("com.acme.ResultOuterClass");
// after
request.setProtobufMessageName("com.acme.ResultRow"); // the GeneratedMessage subclass Defensive patterns
Strategy: type-guard
Validate before calling
// Confirm the named class is a protobuf message before use
Class<?> c = Class.forName(protobufMessageName);
if (!GeneratedMessage.class.isAssignableFrom(c)) {
throw new IllegalArgumentException(protobufMessageName + " is not a protobuf message");
} Type guard
boolean isGeneratedMessage(String name) {
try {
return GeneratedMessage.class.isAssignableFrom(Class.forName(name));
} catch (Throwable t) {
return false;
}
} Try / catch
try {
return stub.query(req);
} catch (StatusRuntimeException e) {
if (String.valueOf(e.getStatus().getDescription()).contains("is not a Protobuf")) {
throw new IllegalArgumentException(req.getProtobufMessageName() + " must be a GeneratedMessage subclass", e);
}
throw e;
} Prevention
- Reference the message class (not the outer class or Builder) when filling protobufMessageName.
- Validate with GeneratedMessage.class.isAssignableFrom in client-side tests.
- Ensure protos are compiled with standard Java message generation, not POJO output.
- Keep generated protobuf sources in version control so names are reviewable.
When it happens
Trigger: Setting protobufMessageName to a valid FQCN of a non-message class — e.g. a protobuf Builder, an Enum wrapper class, a plain POJO, or the generated outer wrapper class (e.g. com.acme.FooOuterClass) instead of the concrete message type.
Common situations: Confusing the outer generated class name with the message class; naming a builder class by appending Builder; loading a class from a jar where the proto was compiled as POJOs (e.g. java_out without lite/full message generation).
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- The Protobuf class [%s] is not known. Is your protobuf jar o
- Invalid parameter type:
- Unauthorized
- Unsupported query result format:
- The gRPC query server requires either a Basic or Anonymous a
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/9c46ec3bb59effd4.
Report an issue: GitHub.