XINCGer/Unity3DTraining · error · InvalidProtocolBufferException
Expected string value for Any.@type
Error message
Expected string value for Any.@type
What it means
Once MergeAny finds the "@type" property name, it expects the next token to be the type URL as a JSON string. If the value of @type is not a StringValue token (e.g. an object, array, number, or null), it throws this InvalidProtocolBufferException. The parser then resolves the type name via the TypeRegistry.
Solutions
- Make @type a plain JSON string: "@type": "type.googleapis.com/pkg.Type".
- Check middleware/serialization layers that may re-encode the @type value into an object or array.
- Validate at ingress that every Any object has @type as a string starting with the expected type URL prefix.
- After the string value, ensure settings.TypeRegistry contains the type name — a missing registration leads to the subsequent descriptor==null failure path.
Example fix
// before: non-string @type
{"detail": {"@type": {"url": "type.googleapis.com/foo.Bar"}}}
// after
{"detail": {"@type": "type.googleapis.com/foo.Bar", "id": 42}} Defensive patterns
Strategy: validation
Validate before calling
// @type value must be a plain string
function validateTypeUrl(obj) {
if (typeof obj?.['@type'] !== 'string') throw new Error('@type must be a string like type.googleapis.com/pkg.Type');
} Type guard
function isStringTypeUrl(v) { return v !== null && typeof v === 'object' && typeof v['@type'] === 'string' && v['@type'].startsWith('type.googleapis.com/'); } Try / catch
try { var msg = JsonParser.Default.Parse<T>(json); }
catch (InvalidProtocolBufferException ex) when (ex.Message == "Expected string value for Any.@type") { /* reject: @type must be a plain string */ } Prevention
- Emit @type as a plain quoted JSON string, never as an object or number
- Ensure middleware does not re-encode @type values
- Register the target types with settings.TypeRegistry so resolution succeeds after @type is parsed
When it happens
Trigger: Parsing an Any field where @type has a non-string value, e.g. {"@type": 123}, {"@type": null}, or {"@type": {"url": "..."}}; also payloads where @type's value got wrapped or array-encoded.
Common situations: Templating bugs that interpolate the type URL incorrectly; serializers emitting type information as a structured object instead of a string; copy-paste mistakes where the @type value was quoted twice or object-encoded by middleware.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Expected object value for Any
- Any message with no @type
- Type registry has no descriptor for type name '
- Expected end of JSON after object
- Expected an object
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/3ccbd04b6beb4555.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/JsonParser.cs:514
// for *this* Any.
while (token.Type != JsonToken.TokenType.Name ||
token.StringValue != JsonFormatter.AnyTypeUrlField ||
tokenizer.ObjectDepth != typeUrlObjectDepth)
{
tokens.Add(token);
token = tokenizer.Next();
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);
}View on GitHub (pinned to 016f98412e)