XINCGer/Unity3DTraining · error · InvalidOperationException

Type registry has no descriptor for type name '

Error message

Type registry has no descriptor for type name '

What it means

Google.Protobuf's JsonParser, when parsing an google.protobuf.Any field, extracts the type name from the '@type' URL and looks it up in the parser's TypeRegistry. If the registry cannot resolve that type name, this InvalidOperationException is thrown because the parser cannot create a message template to merge the Any body into.

Solutions

  1. Register the missing type: JsonParser.Default.WithTypeRegistry(TypeRegistry.FromMessages(typeof(MyMessage))) and parse with that parser instance.
  2. Verify the @type URL's type name matches the registered message's full proto name (package.MessageName).
  3. If the Any payload is unknown to the client, parse the field as a raw Any (it stays unparsed) instead of requesting unpacking, and inspect TypeUrl manually.

Example fix

// before
var msg = JsonParser.Default.Parse<Wrapper>(json);
// after
var registry = TypeRegistry.FromMessages(typeof(MyCustomMessage), typeof(AnotherMessage));
var parser = JsonParser.Default.WithTypeRegistry(registry);
var msg = parser.Parse<Wrapper>(json);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that every @type in the JSON is registered
var registered = TypeRegistry.FromMessages(typeof(MyMessage));
foreach (Match m in Regex.Matches(json, '"@type"\\s*:\\s*"([^"]+)"'))
{
    string typeName = Any.GetTypeName(m.Groups[1].Value);
    if (registered.Find(typeName) == null) throw new Exception($"Unregistered Any type: {typeName}");
}

Type guard

static bool IsAnyTypeRegistered(TypeRegistry registry, string json)
{
    var match = Regex.Match(json, '"@type"\\s*:\\s*"([^"]+)"');
    return match.Success && registry.Find(Any.GetTypeName(match.Groups[1].Value)) != null;
}

Try / catch

try
{
    var msg = parser.Parse<T>(json);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Type registry has no descriptor"))
{
    // fall back to a registry that includes the missing type, or keep Any raw
    logger.LogWarning(ex, "Unknown Any type in payload");
}

Prevention

When it happens

Trigger: Parsing JSON containing an Any field whose @type URL names a message type that was never registered via JsonParser.WithTypeRegistry(...).With(TypeRegistry.FromMessages(...)) — e.g. parsing output from a server that used types not present or not registered on the client.

Common situations: Client/server use different .proto type sets; developer forgets to add custom message types to the TypeRegistry when deserializing Any; type name mismatch between producer and consumer (different package names).

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/JsonParser.cs:522

                if (tokenizer.ObjectDepth < typeUrlObjectDepth)
                {
                    throw new InvalidProtocolBufferException("Any message with no @type");
                }
            }

            // Don't add the @type property or its value to the recorded token list
            token = tokenizer.Next();
            if (token.Type != JsonToken.TokenType.StringValue)
            {
                throw new InvalidProtocolBufferException("Expected string value for Any.@type");
            }
            string typeUrl = token.StringValue;
            string typeName = Any.GetTypeName(typeUrl);

            MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName);
            if (descriptor == null)
            {
                throw new InvalidOperationException("Type registry has no descriptor for type name '" + typeName + "'");
            }

            // Now replay the token stream we've already read and anything that remains of the object, just parsing it
            // as normal. Our original tokenizer should end up at the end of the object.
            var replay = JsonTokenizer.FromReplayedTokens(tokens, tokenizer);
            var body = descriptor.Parser.CreateTemplate();
            if (descriptor.IsWellKnownType)
            {
                MergeWellKnownTypeAnyBody(body, replay);
            }
            else
            {
                Merge(body, replay);
            }
            var data = body.ToByteString();

            // Now that we have the message data, we can pack it into an Any (the message received as a parameter).
            message.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.SetValue(message, typeUrl);

View on GitHub (pinned to 016f98412e)