XINCGer/Unity3DTraining · error · DescriptorValidationException
Property not found in
Error message
Property {propertyName} not found in {ContainingType.ClrType} What it means
CreateAccessor links a descriptor field to the generated C# class by reflection: it looks up a property whose name is derived from the field name on ContainingType.ClrType. If the CLR type has no such property, the descriptor and the generated code are out of sync and a DescriptorValidationException is thrown.
Solutions
- Regenerate the C# code with protoc (and the matching plugin) from the same .proto that produced the descriptor bytes.
- Ensure the descriptor data and the message assembly come from the same schema version.
- Verify messageDescriptor.ClrType passed during construction actually corresponds to the containing message in the descriptor.
- Clean and rebuild so stale generated files are not mixed with new descriptors.
Example fix
// before: stale descriptor paired with regenerated assembly
FileDescriptor.FromGeneratedCode(oldDescriptorData, new[] { dep }, new MessageDescriptor[] { new OldMsg() });
// after: regenerate both from the same .proto
FileDescriptor.FromGeneratedCode(freshDescriptorData, deps, new MessageDescriptor[] { new RegeneratedMsg() }); Defensive patterns
Strategy: validation
Validate before calling
bool ClrTypeMatches(MessageDescriptor md, System.Type clrType) => md.Proto.Field.All(f => clrType.GetProperty(GeneratedPropertyName(f.Name)) != null);
Try / catch
try { FromGeneratedCode(...); } catch (ArgumentException e) when (e.InnerException is DescriptorValidationException dve && dve.Message.Contains("not found in")) { log.LogError("Descriptor/assembly mismatch — regenerate code"); } Prevention
- Regenerate C# code and descriptor bytes together in one build step.
- Never mix generated files from different schema generations.
- Pin protoc/plugin versions in CI.
When it happens
Trigger: FileDescriptor.BuildFrom/FromGeneratedCode where the embedded descriptor contains a field whose generated property does not exist on the passed CLR type (mismatched descriptor vs assembly), or a customClrType/descriptor pair built by hand.
Common situations: Descriptor bytes regenerated (field added/renamed) while the old generated C# assembly is still loaded; passing the wrong message type to descriptor construction; stale plugin output after renaming a field.
Related errors
- Dependencies passed to FileDescriptor.BuildFrom() don't…
- Invalid enum value:
- Unsupported conversion from JSON string for field type
- FieldDescriptors can only be compared to other…
- EnumType is only valid for enum fields.
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/8d969b9b5e28fc11.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/Reflection/FieldDescriptor.cs:377
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...)
if (propertyName == null)
{
return null;
}
var property = ContainingType.ClrType.GetProperty(propertyName);
if (property == null)
{
throw new DescriptorValidationException(this, "Property " + propertyName + " not found in " + ContainingType.ClrType);
}
return IsMap ? new MapFieldAccessor(property, this)
: IsRepeated ? new RepeatedFieldAccessor(property, this)
: (IFieldAccessor)new SingleFieldAccessor(property, this);
}
}
}View on GitHub (pinned to 016f98412e)