grpc-ecosystem/grpc-gateway · error

only primitive and well-known types are allowed in path para

Error message

only primitive and well-known types are allowed in path parameters

What it means

renderServices requires that path parameters be primitive types, enums, or protobuf well-known types. When a non-repeated path parameter targets a GROUP or MESSAGE field that is not a well-known type (e.g. google.protobuf.Timestamp, wrappers), the generator returns "only primitive and well-known types are allowed in path parameters" because such a message cannot be rendered as a valid OpenAPI path parameter.

Source

Thrown at protoc-gen-openapiv2/internal/genopenapi/template.go:1636

					var items *openapiItemsObject
					var minItems *int
					var extensions []extension
					switch pt := parameter.Target.GetType(); pt {
					case descriptorpb.FieldDescriptorProto_TYPE_GROUP, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE:
						if descriptor.IsWellKnownType(parameter.Target.GetTypeName()) {
							if parameter.IsRepeated() {
								return errors.New("only primitive and enum types are allowed in repeated path parameters")
							}
							schema := schemaOfField(parameter.Target, reg, customRefs)
							paramType = schema.Type
							paramFormat = schema.Format
							desc = schema.Description
							defaultValue = schema.Default
							example = schema.Example
							schemaPattern = schema.Pattern
							extensions = schema.extensions
						} else {
							return errors.New("only primitive and well-known types are allowed in path parameters")
						}
					case descriptorpb.FieldDescriptorProto_TYPE_ENUM:
						enum, err := reg.LookupEnum("", parameter.Target.GetTypeName())
						if err != nil {
							return err
						}
						paramType = "string"
						paramFormat = ""
						enumNames = listEnumNames(reg, enum)
						if reg.GetEnumsAsInts() {
							paramType = "integer"
							paramFormat = ""
							enumNames = listEnumNumbers(reg, enum)
						}

						schema := schemaOfField(parameter.Target, reg, customRefs)
						desc = schema.Description
						defaultValue = schema.Default

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Reference a primitive or enum field in the path template instead of the message, e.g. `{book_id}` where book_id is int64/string, or `{book.id}` for a scalar subfield.
  2. Move the message field out of the path into the request body (`body: "*"` or a body field).
  3. If you intended a wrapper, use the actual well-known type (google.protobuf.StringValue, Timestamp, etc.) that IsWellKnownType recognizes.
  4. For IDs, flatten them: put `string id` on the request message and bind `{id}` in the path.

Example fix

// before
rpc GetBook(BookRequest) ... // path: "/v1/{book=**}" where book is message Book
// after
rpc GetBook(GetBookRequest) ... // message { string book_id = 1; } path: "/v1/{book_id}"
Defensive patterns

Strategy: validation

Validate before calling

for _, p := range binding.PathParams {
    t := p.Target.GetType()
    if (t == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE || t == descriptorpb.FieldDescriptorProto_TYPE_GROUP) && !descriptor.IsWellKnownType(p.Target.GetTypeName()) {
        return fmt.Errorf("path param %q must be a primitive, enum, or well-known type", p.Target.GetName())
    }
}

Type guard

func validPathParam(f *descriptor.Field) bool {
    switch f.Target.GetType() {
    case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, descriptorpb.FieldDescriptorProto_TYPE_GROUP:
        return descriptor.IsWellKnownType(f.Target.GetTypeName())
    default:
        return true
    }
}

Try / catch

if err := applyTemplate(...); err != nil {
    if strings.Contains(err.Error(), "well-known types are allowed in path parameters") {
        return fmt.Errorf("rewrite the http rule path to use a scalar field: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A google.api.http path template referencing a message-typed field that is not a well-known type, e.g. `get: "/v1/{book.title}"` where the referenced field is a custom message, or a group field in the path.

Common situations: Pointing a path variable at a nested message/submessage instead of a scalar; using proto2 groups; copying bindings from another service whose field was a well-known wrapper; protos not importing google/protobuf/wrappers.proto or timestamp.proto so the field is a custom message.

Related errors


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