XINCGer/Unity3DTraining · error · DescriptorValidationException

Field with message or enum type missing type_name.

Error message

Field with message or enum type missing type_name.

What it means

During descriptor cross-linking, Google.Protobuf validates every FieldDescriptorProto. If a field's type resolves to FieldType.Message or FieldType.Enum but no type_name was supplied in the .proto/descriptor, the field cannot be linked and CrossLink throws DescriptorValidationException.

Solutions

  1. Ensure every message/enum field's FieldDescriptorProto has Type set AND TypeName set to the fully-qualified type name (e.g. ".pkg.MyMessage").
  2. Regenerate descriptors with protoc instead of constructing them manually.
  3. Validate the descriptor proto before BuildFrom by walking fields and checking TypeName for MESSAGE/ENUM types.
  4. If descriptor data is corrupted, re-embed the descriptor from a fresh protoc run.

Example fix

// before
var field = new FieldDescriptorProto { Name = "item", Number = 1, Type = FieldDescriptorProto.Type.TypeMessage }; // no TypeName
// after
var field = new FieldDescriptorProto { Name = "item", Number = 1, Type = FieldDescriptorProto.Type.TypeMessage, TypeName = ".myproto.Item" };
Defensive patterns

Strategy: validation

Validate before calling

bool ValidDescriptor(FileDescriptorProto f) => f.MessageType.All(m => m.Field.All(fd => fd.Type != FieldDescriptorProto.Type.TypeMessage && fd.Type != FieldDescriptorProto.Type.TypeEnum || !string.IsNullOrEmpty(fd.TypeName)));

Try / catch

try { descriptor.BuildFrom(...); } catch (DescriptorValidationException e) { log.LogError($"Field {e.Message}: check type_name in descriptor"); }

Prevention

When it happens

Trigger: Building a FileDescriptor from a FileDescriptorProto (e.g. FileDescriptor.BuildFrom or FromGeneratedCode) where a message/enum-typed field in a DescriptorProto has FieldType.Message or FieldType.Enum but proto.type_name is null or empty.

Common situations: Hand-crafted or programmatically mutated FileDescriptorProtos; descriptors serialized by a non-protoc tool that omitted type_name; truncated or manually edited descriptor bytes embedded in generated code.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12). Data as JSON: /api/errors/49fdbedc990ba0ae. Report an issue: GitHub.

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/Reflection/FieldDescriptor.cs:351

                }
                else if (fieldType == FieldType.Enum)
                {
                    if (!(typeDescriptor is EnumDescriptor))
                    {
                        throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not an enum type.");
                    }
                    enumType = (EnumDescriptor)typeDescriptor;
                }
                else
                {
                    throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
                }
            }
            else
            {
                if (fieldType == FieldType.Message || fieldType == FieldType.Enum)
                {
                    throw new DescriptorValidationException(this, "Field with message or enum type missing type_name.");
                }
            }

            // Note: no attempt to perform any default value parsing

            File.DescriptorPool.AddFieldByNumber(this);

            if (ContainingType != null && ContainingType.Proto.Options != null && ContainingType.Proto.Options.MessageSetWireFormat)
            {
                throw new DescriptorValidationException(this, "MessageSet format is not supported.");
            }
            accessor = CreateAccessor();
        }

        private IFieldAccessor CreateAccessor()
        {
            // If we're given no property name, that's because we really don't want an accessor.
            // (At the moment, that means it's a map entry message...)

View on GitHub (pinned to 016f98412e)