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 = use

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Change the path template of one of the conflicting rpcs to a unique pattern
  2. Differentiate resources via distinct template variables (e.g. '/v1/items/{id}' vs '/v1/items')
  3. Remove the redundant rpc or annotation
  4. 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

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


AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02). Data as JSON: /api/errors/6ea867aaf969fe07. Report an issue: GitHub.