quarkusio/quarkus · error · IllegalStateException

Type already defined: ${fullyQualifiedTypeName}

Error message

Type already defined: ${fullyQualifiedTypeName}

What it means

Reflection index building records every message type (Descriptors.Descriptor) by its fully qualified name. processType throws this IllegalStateException if a type with that full name (package.MessageName, including nested types) was already registered by a previously processed FileDescriptor or nested type.

Source

Thrown at extensions/grpc/reflection/src/main/java/io/quarkus/grpc/reflection/service/GrpcServerIndex.java:144

            throw new IllegalStateException("Service already defined: " + fullyQualifiedServiceName);
        }
        descriptorsBySymbol.put(fullyQualifiedServiceName, fd);
        for (Descriptors.MethodDescriptor method : service.getMethods()) {
            String fullyQualifiedMethodName = method.getFullName();
            if (descriptorsBySymbol.containsKey(fullyQualifiedMethodName)) {
                throw new IllegalStateException(
                        "Method already defined: " + fullyQualifiedMethodName + " in " + fullyQualifiedServiceName);
            }
            descriptorsBySymbol.put(fullyQualifiedMethodName, fd);
        }
    }

    private void processType(Descriptors.Descriptor type, FileDescriptor fd,
            Map<String, FileDescriptor> descriptorsBySymbol,
            Map<String, Map<Integer, FileDescriptor>> descriptorsByExtensionAndNumber) {
        String fullyQualifiedTypeName = type.getFullName();
        if (descriptorsBySymbol.containsKey(fullyQualifiedTypeName)) {
            throw new IllegalStateException("Type already defined: " + fullyQualifiedTypeName);
        }
        descriptorsBySymbol.put(fullyQualifiedTypeName, fd);
        for (Descriptors.FieldDescriptor extension : type.getExtensions()) {
            processExtension(extension, fd, descriptorsByExtensionAndNumber);
        }
        for (Descriptors.Descriptor nestedType : type.getNestedTypes()) {
            processType(nestedType, fd, descriptorsBySymbol, descriptorsByExtensionAndNumber);
        }
    }

    private void processExtension(Descriptors.FieldDescriptor extension, FileDescriptor fd,
            Map<String, Map<Integer, FileDescriptor>> descriptorsByExtensionAndNumber) {
        String extensionName = extension.getContainingType().getFullName();
        int extensionNumber = extension.getNumber();

        descriptorsByExtensionAndNumber.computeIfAbsent(extensionName,
                new Function<String, Map<Integer, FileDescriptor>>() {
                    @Override

View on GitHub (pinned to e1c734241f)

Solutions

  1. Never re-vendor protos already shipped by dependencies (e.g. don't copy google/protobuf/empty.proto); import them instead.
  2. Give duplicate messages distinct proto packages or names.
  3. Exclude the dependency shipping the duplicate proto (mvn dependency:tree + exclusion).
  4. Clean rebuild to flush stale generated descriptors.

Example fix

// before: your own src/main/proto/google/protobuf/empty.proto
// after: delete it and use
import "google/protobuf/empty.proto";
Defensive patterns

Strategy: validation

Validate before calling

// lint: ensure no message full name is declared twice across protos
Set<String> msgNames = new HashSet<>();
for (ProtoFile f : allProtos)
  for (MessageDef m : f.getMessages())
    if (!msgNames.add(f.getPackage() + "." + m.getName()))
      throw new IllegalStateException("Duplicate message: " + m.getName());

Try / catch

try {
    startServer();
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Type already defined:")) {
        // locate the duplicate message declaration (name in message) and de-duplicate
    } else throw e;
}

Prevention

When it happens

Trigger: Two proto files declare messages with the same fully qualified name (same proto package, same message name); nested types repeated across duplicated descriptors.

Common situations: Vendoring/copying a proto (e.g. google/protobuf/*.proto or well-known types) into your own package, colliding with a dependency that also ships it; two SDK versions bundling the same message protos.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/e7d6bb7af153e123. Report an issue: GitHub.