grpc-ecosystem/grpc-gateway · error
duplicate annotation: method=%s, template=%s
Error message
duplicate annotation: method=%s, template=%s
What it means
CheckDuplicateAnnotation guards against two HTTP rule annotations sharing the same method+path template within a service. Duplicate mappings would generate ambiguous routes, so the registry records each (method, template, service) and errors on re-registration.
Source
Thrown at internal/descriptor/registry.go:859
}
// GetOpenAPIFieldOption returns a registered OpenAPI option for a field
func (r *Registry) GetOpenAPIFieldOption(qualifiedField string) (*options.JSONSchema, bool) {
opt, ok := r.fieldOptions[qualifiedField]
return opt, ok
}
func (r *Registry) FieldName(f *Field) string {
if r.useJSONNamesForFields {
return f.GetJsonName()
}
return f.GetName()
}
func (r *Registry) CheckDuplicateAnnotation(httpMethod string, httpTemplate string, svc *Service) error {
a := annotationIdentifier{method: httpMethod, pathTemplate: httpTemplate, service: svc}
if _, ok := r.annotationMap[a]; ok {
return fmt.Errorf("duplicate annotation: method=%s, template=%s", httpMethod, httpTemplate)
}
r.annotationMap[a] = struct{}{}
return nil
}
// SetDisableServiceTags sets disableServiceTags
func (r *Registry) SetDisableServiceTags(use bool) {
r.disableServiceTags = use
}
// GetDisableServiceTags returns disableServiceTags
func (r *Registry) GetDisableServiceTags() bool {
return r.disableServiceTags
}
// SetDisableDefaultResponses sets disableDefaultResponses
func (r *Registry) SetDisableDefaultResponses(use bool) {
r.disableDefaultResponses = useView on GitHub (pinned to a58a4436a3)
Solutions
- Change the path template of one of the conflicting rpcs to a unique pattern
- Differentiate resources via distinct template variables (e.g. '/v1/items/{id}' vs '/v1/items')
- Remove the redundant rpc or annotation
- Review the service's google.api.http annotations for copy-paste leftovers
Example fix
// before
rpc GetBook(...) returns (...) { option (google.api.http) = { get: "/v1/books/{id}" }; }
rpc ListBooks(...) returns (...) { option (google.api.http) = { get: "/v1/books/{id}" }; }
// after
rpc ListBooks(...) returns (...) { option (google.api.http) = { get: "/v1/books" }; } Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]bool{}
for _, rpc := range svc.Rpcs {
key := httpMethod(rpc) + " " + pathTemplate(rpc)
if seen[key] { return fmt.Errorf("duplicate HTTP mapping in service %s: %s", svc.GetName(), key) }
seen[key] = true
}
err = reg.CheckDuplicateAnnotation(method, template, svc) Prevention
- Lint google.api.http annotations for duplicate verb+path pairs in CI (buf lint does this)
- Never copy an annotation without changing the path template
- Design resource paths so every rpc maps to a unique method+URL combination
When it happens
Trigger: A proto service has two rpcs annotated with the same HTTP verb and identical path pattern (e.g. two 'get' rules for '/v1/items'); calling CheckDuplicateAnnotation twice with the same httpMethod/httpTemplate for a service.
Common situations: Copy-pasting google.api.http annotations between rpcs and forgetting to change the path; template parameters renamed but base path left identical; merging branches that each added an rpc on the same route.
Related errors
- must not set request body when http method is GET: %s
- must not set request body when http method is DELETE except
- no target service defined in the file
- only primitive and enum types are allowed in repeated path p
- only primitive and well-known types are allowed in path para
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/6ea867aaf969fe07.
Report an issue: GitHub.