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

  1. Sanitize generated names: replace invalid characters with '_' and prefix with a letter if the name starts with a digit.
  2. Validate names against ^[_A-Za-z][_A-Za-z0-9]*$ before adding them to a DescriptorProto/FieldDescriptorProto.
  3. Fix the source .proto file if it (impossibly, via old tooling) contains an invalid identifier, then re-run protoc.
  4. 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

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


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)