XINCGer/Unity3DTraining · error · DescriptorValidationException
FieldDescriptorProto.oneof_index is out of range for type
Error message
FieldDescriptorProto.oneof_index is out of range for type {parent.Name} What it means
Google.Protobuf throws this DescriptorValidationException while building a FieldDescriptor when the proto's oneof_index does not point at a valid entry in the parent message's oneof declaration list. A field that declares it belongs to a oneof must reference an existing oneof_decl; an index of -1 means 'no oneof' and is handled specially. Any other out-of-range index means the file descriptor is internally inconsistent.
Solutions
- Regenerate the descriptor from the authoritative .proto source with protoc so oneof_index values are recomputed consistently.
- If building descriptors in code, add all OneofDecl entries to the message proto BEFORE appending fields whose OneofIndex refers to them.
- Validate each field's OneofIndex (-1 or 0..OneofDecl.Count-1) against the parent's OneofDecl list before deserializing/constructing descriptors from untrusted data.
Example fix
// before: field added before its oneof decl
msg.Field.Add(new FieldDescriptorProto { Name = "val", OneofIndex = 0 });
msg.OneofDecl.Add(new OneofDescriptorProto { Name = "choice" });
// after: declare the oneof first
msg.OneofDecl.Add(new OneofDescriptorProto { Name = "choice" });
msg.Field.Add(new FieldDescriptorProto { Name = "val", OneofIndex = 0 }); Defensive patterns
Strategy: validation
Validate before calling
bool ok = field.Proto.OneofIndex == -1
|| (field.Proto.OneofIndex >= 0 && field.Proto.OneofIndex < parent.Proto.OneofDecl.Count);
if (!ok) throw new InvalidDataException($"oneof_index {field.Proto.OneofIndex} out of range for {parent.Name}"); Try / catch
try { descriptor = FileDescriptor.BuildFromByteStrings(data); }
catch (DescriptorValidationException ex) { log.LogError(ex, "Invalid descriptor: " + ex.Message); }
// DescriptorValidationException.Message includes the offending field name. Prevention
- Declare oneofs before fields in programmatically built protos.
- Always regenerate descriptors with protoc instead of editing serialized bytes.
- Validate untrusted descriptor payloads against expected message schemas before use.
When it happens
Trigger: Building descriptor objects from a FileDescriptorSet/proto bytes where a field's oneof_index is negative (other than -1) or >= parent.Proto.OneofDecl.Count, e.g. a hand-edited or corrupted serialized FileDescriptorProto, or programmatically constructed protos where Oneofs were added to the parent after the fields referencing them by index.
Common situations: Manually assembling FileDescriptorProto in code and appending fields before their oneof declarations; regenerating descriptors with mismatched toolchain versions; corrupt or tampered descriptor payloads received over the network (this plugin ships in a Unity socket/networking project).
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Property Case not found in
- Method Clear not found in
- Multiple values specified for oneof
- Invalid type specified
- "TypeName" is not a type.
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/e38b8e8537e9b7c1.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/Reflection/FieldDescriptor.cs:87
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
Proto = proto;
if (proto.Type != 0)
{
fieldType = GetFieldTypeFromProtoType(proto.Type);
}
if (FieldNumber <= 0)
{
throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
}
ContainingType = parent;
// OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
if (proto.OneofIndex != -1)
{
if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
{
throw new DescriptorValidationException(this, "FieldDescriptorProto.oneof_index is out of range for type " + parent.Name);
}
ContainingOneof = parent.Oneofs[proto.OneofIndex];
}
file.DescriptorPool.AddSymbol(this);
// We can't create the accessor until we've cross-linked, unfortunately, as we
// may not know whether the type of the field is a map or not. Remember the property name
// for later.
// We could trust the generated code and check whether the type of the property is
// a MapField, but that feels a tad nasty.
this.propertyName = propertyName;
JsonName = Proto.JsonName == "" ? JsonFormatter.ToJsonName(Proto.Name) : Proto.JsonName;
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>View on GitHub (pinned to 016f98412e)