XINCGer/Unity3DTraining · error · InvalidProtocolBufferException
Expected an object
Error message
Expected an object
What it means
JsonParser.Merge expects the next token for a message to be a start-object token ('{'), since a protobuf message maps to a JSON object. Well-known types (Timestamp, wrappers, Any, etc.) get special handling first; anything left falls through to this check and throws InvalidProtocolBufferException if the token is not '{'.
Solutions
- Confirm the JSON input is a top-level object '{ ... }' matching the target message type
- Parse scalar/array values into the matching field, not into the message itself
- Check that the target message type matches the JSON producer's schema version
- Inspect the raw JSON (log first 100 chars) to see what token actually appears
Example fix
// before
var msg = parser.Parse<User>("[{'id':1}]"); // array, not object
// after
var msg = parser.Parse<User>("{'id': 1}"); Defensive patterns
Strategy: validation
Validate before calling
static bool IsJsonObject(string json) { var t = json.TrimStart(); return t.StartsWith("{"); } Try / catch
try { return parser.Parse<T>(json); } catch (InvalidProtocolBufferException ex) when (ex.Message == "Expected an object") { log.Error($"Expected JSON object for {typeof(T).Name}"); throw new BadRequestException("Message JSON must be an object", ex); } Prevention
- Confirm the JSON shape matches the target message type before parsing
- Parse field values into their own types, not into the message
- Log a snippet of the input on failure to identify arrays/scalars passed by mistake
When it happens
Trigger: Parsing a JSON array, string, number, or null where a message object is expected — e.g. passing the JSON for a field's value directly to a top-level Parse call, or assigning a scalar JSON value to a message-typed field.
Common situations: Passing a JSON array of messages to Parse instead of a single object; wiring JSON produced for a nested field to the wrong message type; a client sending null or a quoted string where an object body was expected; wrong wrapper-type assumptions.
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
- Unsupported JSON token type
- Unsupported conversion from JSON number for field type
- Expected string value for Duration
- Expected string value for FieldMask
- Expected end of JSON after object
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/17cf879b7af795fc.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/JsonParser.cs:168
{
if (tokenizer.ObjectDepth > settings.RecursionLimit)
{
throw InvalidProtocolBufferException.JsonRecursionLimitExceeded();
}
if (message.Descriptor.IsWellKnownType)
{
Action<JsonParser, IMessage, JsonTokenizer> handler;
if (WellKnownTypeHandlers.TryGetValue(message.Descriptor.FullName, out handler))
{
handler(this, message, tokenizer);
return;
}
// Well-known types with no special handling continue in the normal way.
}
var token = tokenizer.Next();
if (token.Type != JsonToken.TokenType.StartObject)
{
throw new InvalidProtocolBufferException("Expected an object");
}
var descriptor = message.Descriptor;
var jsonFieldMap = descriptor.Fields.ByJsonName();
// All the oneof fields we've already accounted for - we can only see each of them once.
// The set is created lazily to avoid the overhead of creating a set for every message
// we parsed, when oneofs are relatively rare.
HashSet<OneofDescriptor> seenOneofs = null;
while (true)
{
token = tokenizer.Next();
if (token.Type == JsonToken.TokenType.EndObject)
{
return;
}
if (token.Type != JsonToken.TokenType.Name)
{
throw new InvalidOperationException("Unexpected token type " + token.Type);
}View on GitHub (pinned to 016f98412e)