grpc-ecosystem/grpc-gateway · error

failed to lookup message type %s: %w

Error message

failed to lookup message type %s: %w

What it means

In protoc-gen-grpc-gateway's body-field type resolution (GetBodyFieldType), when a body mapping path component targets a message-typed field, the registry is asked to resolve that type name relative to the parent message. If LookupMsg cannot find the message (unregistered file, wrong name, load-order issue), the code wraps the failure with 'failed to lookup message type %s: %w'. It signals the proto descriptor graph is incomplete or inconsistent for the body path being generated.

Source

Thrown at protoc-gen-grpc-gateway/internal/gengateway/template.go:66

	return "", errors.New("no body field found")
}

// GetBodyFieldType returns the Go type of the body field.
func (b binding) GetBodyFieldType() (string, error) {
	if b.Body == nil || len(b.Body.FieldPath) == 0 {
		return "", errors.New("no body field found")
	}

	lastComponent := b.Body.FieldPath[len(b.Body.FieldPath)-1]
	fieldType := lastComponent.Target.GetType()

	// Handle message types
	if fieldType == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
		// Get the parent message to provide proper lookup context
		parentMsg := lastComponent.Target.Message
		msg, err := b.Registry.LookupMsg(parentMsg.FQMN(), lastComponent.Target.GetTypeName())
		if err != nil {
			return "", fmt.Errorf("failed to lookup message type %s: %w", lastComponent.Target.GetTypeName(), err)
		}
		return msg.GoType(b.Method.Service.File.GoPkg.Path), nil
	}

	return "", errors.New("unsupported body field type")
}

// HasQueryParam determines if the binding needs parameters in query string.
//
// It sometimes returns true even though actually the binding does not need.
// But it is not serious because it just results in a small amount of extra codes generated.
func (b binding) HasQueryParam() bool {
	if b.Body != nil && len(b.Body.FieldPath) == 0 {
		return false
	}
	fields := make(map[string]bool)
	for _, f := range b.Method.RequestType.Fields {
		fields[f.GetName()] = true

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Ensure every proto dependency (including transitive ones) is passed to protoc so its descriptors are registered with the Registry before generation.
  2. Regenerate descriptors so the body path's message type name matches the current proto (check lastComponent.Target.GetTypeName() against your .proto files).
  3. If using RegisterFunc/registry loading, verify LookupMsg-able messages exist — e.g. confirm the message FQMN appears in the registry with a small debug print or registry lookup in a custom plugin.
  4. Update protoc-gen-grpc-gateway to match your protoc/protoc-gen-go versions and regenerate.

Example fix

// before: protoc foo.proto only, message Bar lives in bar.proto used as body field
protoc --grpc-gateway_out=. foo.proto

// after: pass all deps
protoc -I . --grpc-gateway_out=. foo.proto bar.proto
Defensive patterns

Strategy: validation

Validate before calling

// In a custom plugin/wrapper, before generating:
for _, dep := range allProtoFiles {
    if _, err := reg.LookupMsg(parentFQMN, typeName); err != nil {
        return fmt.Errorf("missing message %s (from %s): %w", typeName, dep, err)
    }
}

Prevention

When it happens

Trigger: Calling GetBodyFieldType (during gateway template execution) when lastComponent.Target is TYPE_MESSAGE and Registry.LookupMsg(parentMsg.FQMN(), typeName) fails — e.g. the message's file was never loaded into the Registry, the type name is stale, or external deps weren't imported via WithImportPath/LoadFromPlugin.

Common situations: Generating a gateway for protos that depend on external packages (googleapis annotations, well-known types) without registering those dependency files; renaming/moving a message so body path references no longer resolve; using a plugin version mismatched with regenerated descriptors.

Related errors


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