quarkusio/quarkus · error · IllegalStateException
Duplicated gRPC service
Error message
Duplicated gRPC service: ${serviceName} What it means
When building the reflection index of all gRPC services served by the server, each ServiceDescriptor's name must be unique. If two service descriptors share the same service name, GrpcServerIndex's constructor throws this IllegalStateException instead of building an ambiguous reflection index.
Solutions
- Find the duplicate: enable debug logging on io.quarkus.grpc.reflection and inspect registered services; identify which proto produces the duplicate name.
- Ensure each proto service has a unique package (package a.b; in the .proto) so full service names don't collide.
- Remove duplicate service bean registrations — a proto service should map to exactly one implementation bean.
- If two versions of the same proto are on the classpath (e.g. via a transitive dependency), exclude the older artifact.
Example fix
// before: two protos both declaring
package foo;
service Greeter {...}
// after
package foo.v1;
service Greeter {...} // unique full name foo.v1.Greeter Defensive patterns
Strategy: try-catch
Type guard
boolean hasDuplicate(List<String> names) {
return names.size() != new HashSet<>(names).size();
} Try / catch
try {
startServer();
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Duplicated gRPC service:")) {
// inspect service bean registration and proto packages for the duplicated name
} else throw e;
} Prevention
- One proto service = one implementation bean; don't double-register
- Use distinct proto packages for services with the same name
- Exclude older artifacts shipping the same proto
- Review @GrpcService-annotated beans for accidental duplicates
When it happens
Trigger: Registering two gRPC services (or the same service implementation twice, e.g. two beans exposed as the same proto service, or a service registered both directly and via a different port/bean) so io.grpc.ServerServiceDefinition entries contain a duplicated service name.
Common situations: Declaring the same service bean twice (e.g. @GrpcService plus manual registration); two different proto packages compiled to the same service full name; accidentally shipping the same proto twice under different files; running reflection against a server with duplicated service registrations from two versions of the same proto.
Related errors
- Blocking gRPC client call made from the event loop. If the…
- Cache key generator instantiation failed
- Cannot instantiate JSON-B component:
- Cannot transform exception
- Cannot transform exception
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/2b38d05b5eba2716.
Report an issue: GitHub.
Appendix: source
Thrown at extensions/grpc/reflection/src/main/java/io/quarkus/grpc/reflection/service/GrpcServerIndex.java:49
List<ServerServiceDefinition> definitions) {
Queue<FileDescriptor> fileDescriptorsToProcess = new ArrayDeque<>();
Set<String> files = new HashSet<>();
Set<String> names = new HashSet<>();
Set<FileDescriptor> services = new HashSet<>();
Map<String, FileDescriptor> descriptorsByName = new LinkedHashMap<>();
Map<String, FileDescriptor> descriptorsBySymbol = new LinkedHashMap<>();
Map<String, Map<Integer, FileDescriptor>> descriptorsByExtensionAndNumber = new LinkedHashMap<>();
// Collect the services
for (ServerServiceDefinition definition : definitions) {
ServiceDescriptor serviceDescriptor = definition.getServiceDescriptor();
if (serviceDescriptor.getSchemaDescriptor() instanceof ProtoFileDescriptorSupplier) {
ProtoFileDescriptorSupplier supplier = (ProtoFileDescriptorSupplier) serviceDescriptor
.getSchemaDescriptor();
FileDescriptor fd = supplier.getFileDescriptor();
String serviceName = serviceDescriptor.getName();
if (names.contains(serviceName)) {
throw new IllegalStateException("Duplicated gRPC service: " + serviceName);
}
services.add(fd);
names.add(serviceName);
if (!files.contains(fd.getName())) {
files.add(fd.getName());
fileDescriptorsToProcess.add(fd);
}
}
}
// Traverse the set of service and add dependencies
while (!fileDescriptorsToProcess.isEmpty()) {
FileDescriptor fd = fileDescriptorsToProcess.remove();
processFileDescriptor(fd, descriptorsByName, descriptorsBySymbol, descriptorsByExtensionAndNumber);
for (FileDescriptor dep : fd.getDependencies()) {
if (!files.contains(dep.getName())) {
files.add(dep.getName());View on GitHub (pinned to e1c734241f)