XINCGer/Unity3DTraining · error · DescriptorValidationException

Enums must contain at least one value.

Error message

Enums must contain at least one value.

What it means

EnumDescriptor construction throws DescriptorValidationException when an EnumDescriptorProto contains zero values. Protobuf requires every enum to declare at least one value because fields of that enum type need a valid default value (the first declared value, which must be 0 in proto3).

Solutions

  1. Add at least one enum value, conventionally a zero-valued default (proto3 requires the first value to have number 0).
  2. If building descriptors programmatically, add an EnumValueProto entry before building the FileDescriptor.
  3. Inspect the serialized descriptor set for enums with empty 'value' lists and fix the producer tooling.
  4. Validate descriptor protos before building (assert proto.Value.Count > 0 for every enum).

Example fix

// before
var e = new EnumDescriptorProto { Name = "Status" }; // no values
fileProto.EnumTypes.Add(e);
// after
var e = new EnumDescriptorProto
{
    Name = "Status",
    Values = { new EnumValueDescriptorProto { Name = "STATUS_UNKNOWN", Number = 0 } }
};
fileProto.EnumTypes.Add(e);
Defensive patterns

Strategy: validation

Validate before calling

static void AssertEnumsHaveValues(FileDescriptorProto file)
{
    foreach (var e in file.EnumTypes)
        if (e.Values.Count == 0)
            throw new InvalidOperationException($"Enum {e.Name} has no values");
}

Type guard

bool HasValues(EnumDescriptorProto e) => e.Value.Count > 0;

Try / catch

try
{
    var fd = FileDescriptor.BuildFromByteString(bytes, dependencies);
}
catch (DescriptorValidationException ex) when (ex.Message.Contains("at least one value"))
{
    // reject the descriptor set / add a default zero value and rebuild
}

Prevention

When it happens

Trigger: Building a FileDescriptor from an EnumDescriptorProto with an empty Value collection — e.g. new EnumDescriptorProto { Name = "E" } with no values added, a serialized FileDescriptorSet whose enum has no EnumValueProto entries, or a custom codegen emitting empty enums.

Common situations: Dynamically generating proto descriptors and forgetting to seed a default enum value; tooling stripping all enum values during schema transformation; tests constructing minimal descriptor scaffolding without populating values.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/Reflection/EnumDescriptor.cs:59

    public sealed class EnumDescriptor : DescriptorBase
    {
        private readonly EnumDescriptorProto proto;
        private readonly MessageDescriptor containingType;
        private readonly IList<EnumValueDescriptor> values;
        private readonly Type clrType;

        internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type clrType)
            : base(file, file.ComputeFullName(parent, proto.Name), index)
        {
            this.proto = proto;
            this.clrType = clrType;
            containingType = parent;

            if (proto.Value.Count == 0)
            {
                // We cannot allow enums with no values because this would mean there
                // would be no valid default value for fields of this type.
                throw new DescriptorValidationException(this, "Enums must contain at least one value.");
            }

            values = DescriptorUtil.ConvertAndMakeReadOnly(proto.Value,
                                                           (value, i) => new EnumValueDescriptor(value, file, this, i));

            File.DescriptorPool.AddSymbol(this);
        }

        internal EnumDescriptorProto Proto { get { return proto; } }

        /// <summary>
        /// The brief name of the descriptor's target.
        /// </summary>
        public override string Name { get { return proto.Name; } }

        /// <summary>
        /// The CLR type for this enum. For generated code, this will be a CLR enum type.
        /// </summary>

View on GitHub (pinned to 016f98412e)