grpc/grpc-java · error · IllegalStateException
No method bound for descriptor entry ${fullMethodName}
Error message
No method bound for descriptor entry ${fullMethodName} What it means
During ServerServiceDefinition.build(), every MethodDescriptor declared in the ServiceDescriptor must have a corresponding bound ServerMethodDefinition with the identical descriptor instance. If a descriptor method has no bound entry, the builder throws IllegalStateException with 'No method bound for descriptor entry <fullMethodName>'. This is an internal consistency check ensuring the service descriptor and the dispatched method implementations match.
Source
Thrown at api/src/main/java/io/grpc/ServerServiceDefinition.java:137
/**
* Construct new ServerServiceDefinition.
*/
public ServerServiceDefinition build() {
ServiceDescriptor serviceDescriptor = this.serviceDescriptor;
if (serviceDescriptor == null) {
List<MethodDescriptor<?, ?>> methodDescriptors
= new ArrayList<>(methods.size());
for (ServerMethodDefinition<?, ?> serverMethod : methods.values()) {
methodDescriptors.add(serverMethod.getMethodDescriptor());
}
serviceDescriptor = new ServiceDescriptor(serviceName, methodDescriptors);
}
Map<String, ServerMethodDefinition<?, ?>> tmpMethods = new HashMap<>(methods);
for (MethodDescriptor<?, ?> descriptorMethod : serviceDescriptor.getMethods()) {
ServerMethodDefinition<?, ?> removed = tmpMethods.remove(
descriptorMethod.getFullMethodName());
if (removed == null) {
throw new IllegalStateException(
"No method bound for descriptor entry " + descriptorMethod.getFullMethodName());
}
if (removed.getMethodDescriptor() != descriptorMethod) {
throw new IllegalStateException(
"Bound method for " + descriptorMethod.getFullMethodName()
+ " not same instance as method in service descriptor");
}
}
if (tmpMethods.size() > 0) {
throw new IllegalStateException(
"No entry in descriptor matching bound method "
+ tmpMethods.values().iterator().next().getMethodDescriptor().getFullMethodName());
}
return new ServerServiceDefinition(serviceDescriptor, methods);
}
}
}
View on GitHub (pinned to 64daddc1f3)
Solutions
- Add the missing method with builder().addMethod(ServerMethodDefinition or MethodDescriptor) so every descriptor method is bound
- Generate both the ServiceDescriptor and ServerServiceDefinition from the same source (protoc-generated code) instead of hand-assembling
- Verify each MethodDescriptor's full method name (service + method) matches exactly between the descriptor and the bound method
- Re-run code generation after changing the .proto so descriptor and bindings stay in sync
Example fix
// before: descriptor declares Foo but only Bar was added
ServerServiceDefinition.newBuilder(serviceDescriptor)
.addMethod(barMethod)
.build(); // IllegalStateException: No method bound for descriptor entry pkg.Svc/Foo
// after
ServerServiceDefinition.newBuilder(serviceDescriptor)
.addMethod(barMethod)
.addMethod(fooMethod)
.build(); Defensive patterns
Strategy: validation
Validate before calling
// validate binding completeness before build()
ServiceDescriptor sd = serviceDescriptor;
Set<String> bound = new HashSet<>();
for (ServerMethodDefinition<?,?> m : methods) bound.add(m.getMethodDescriptor().getFullMethodName());
for (MethodDescriptor<?,?> d : sd.getMethods()) {
if (!bound.contains(d.getFullMethodName())) {
throw new IllegalStateException("Unbound descriptor method: " + d.getFullMethodName());
}
} Try / catch
try {
definition = builder.build();
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("No method bound for descriptor entry")) {
logger.error("Descriptor/binding mismatch: {}", e.getMessage());
throw new IllegalArgumentException("Regenerate service code; bind all descriptor methods", e);
}
throw e;
} Prevention
- Generate ServiceDescriptor and ServerServiceDefinition together from protoc codegen
- Never hand-edit generated descriptors; re-run codegen after .proto changes
- Ensure every addMethod uses a MethodDescriptor whose full method name matches the service descriptor's
- Add a unit test that builds all service definitions at startup
When it happens
Trigger: Constructing a ServerServiceDefinition via builder().addMethod(descriptor-with-service-name) while the ServiceDescriptor contains a method whose full method name was never added (e.g. methods list and serviceDescriptor built from different MethodDescriptor sets, custom service descriptor with a method name not bound); programmatic assembly where a method descriptor's service name differs so the lookup by fullMethodName misses.
Common situations: Hand-writing ServiceDescriptor and ServerServiceDefinition separately and adding a new RPC to one but not the other; codegen mismatches or manually modified descriptors; typos in method names causing full-method-name mismatch between the two collections.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Metric with name ${name} already exists
- ScheduledExecutorService not set in Builder
- ChannelLogger is not set in Builder
- NameResolverRegistry is not set in Builder
- Framer already closed
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/1e7b86cc589dbee3.
Report an issue: GitHub.