XINCGer/Unity3DTraining · error · DescriptorValidationException
" " is not a valid identifier.
Error message
"{descriptor.Name}" is not a valid identifier. What it means
DescriptorPool.ValidateSymbolName throws DescriptorValidationException when a descriptor's Name is non-empty but not a valid protobuf identifier. Valid names must match ^[_A-Za-z][_A-Za-z0-9]*$ — start with a letter or underscore, then only letters, digits, or underscores. Note that in practice protoc already rejects such names, so this usually indicates hand-built or tampered descriptors.
Solutions
- Sanitize generated names: replace invalid characters with '_' and prefix with a letter if the name starts with a digit.
- Validate names against ^[_A-Za-z][_A-Za-z0-9]*$ before adding them to a DescriptorProto/FieldDescriptorProto.
- Fix the source .proto file if it (impossibly, via old tooling) contains an invalid identifier, then re-run protoc.
- If names come from user input, map them to a whitelist of valid identifiers.
Example fix
// before
var field = new FieldDescriptorProto { Name = "first-name", Number = 1, Type = FieldDescriptorProto.Types.Type.String };
// after
var field = new FieldDescriptorProto { Name = "first_name", Number = 1, Type = FieldDescriptorProto.Types.Type.String }; Defensive patterns
Strategy: validation
Validate before calling
static readonly Regex Ident = new Regex("^[_A-Za-z][_A-Za-z0-9]*$");
static string SanitizeName(string raw)
{
var s = new string(raw.Select(c => char.IsLetterOrDigit(c) || c == '_' ? c : '_').ToArray());
if (s.Length == 0 || char.IsDigit(s[0])) s = "_" + s;
return s;
}
// apply: field.Name = SanitizeName(externalName); Type guard
bool IsValidIdentifier(string name) =>
!string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[_A-Za-z][_A-Za-z0-9]*$"); Try / catch
try
{
var fd = FileDescriptor.BuildFromByteString(bytes, dependencies);
}
catch (DescriptorValidationException ex) when (ex.Message.Contains("is not a valid identifier"))
{
// sanitize the offending declaration name and rebuild
} Prevention
- Sanitize any externally derived names before using them as proto identifiers
- Validate with ^[_A-Za-z][_A-Za-z0-9]*$ at descriptor-construction time
- Lint generated descriptors in CI
When it happens
Trigger: Building descriptors from a FileDescriptorProto where a declaration's name starts with a digit (e.g. "2fa"), contains a hyphen or dot ("my-field", "a.b"), or contains whitespace — typically from programmatically constructed DescriptorProtos or dynamically generated names derived from unvalidated input.
Common situations: Generating message/field names from external data (table names, HTTP header names with hyphens, JSON keys with special characters) without sanitizing; post-processing descriptor sets with scripts that mangle names; dynamic schema builders allowing arbitrary strings.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Missing name.
- Field numbers must be positive integers.
- " " is already defined (as something other than a package)…
- " " is already defined in file " ".
- Field number has already been used in " " by field " ".
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/e5afd77097c744ac.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/Reflection/DescriptorPool.cs:200
}
private static readonly Regex ValidationRegex = new Regex("^[_A-Za-z][_A-Za-z0-9]*$",
FrameworkPortability.CompiledRegexWhereAvailable);
/// <summary>
/// Verifies that the descriptor's name is valid (i.e. it contains
/// only letters, digits and underscores, and does not start with a digit).
/// </summary>
/// <param name="descriptor"></param>
private static void ValidateSymbolName(IDescriptor descriptor)
{
if (descriptor.Name == "")
{
throw new DescriptorValidationException(descriptor, "Missing name.");
}
if (!ValidationRegex.IsMatch(descriptor.Name))
{
throw new DescriptorValidationException(descriptor,
"\"" + descriptor.Name + "\" is not a valid identifier.");
}
}
/// <summary>
/// Returns the field with the given number in the given descriptor,
/// or null if it can't be found.
/// </summary>
internal FieldDescriptor FindFieldByNumber(MessageDescriptor messageDescriptor, int number)
{
FieldDescriptor ret;
fieldsByNumber.TryGetValue(new DescriptorIntPair(messageDescriptor, number), out ret);
return ret;
}
internal EnumValueDescriptor FindEnumValueByNumber(EnumDescriptor enumDescriptor, int number)
{
EnumValueDescriptor ret;View on GitHub (pinned to 016f98412e)