Expected BEGIN_OBJECT but was STRING

ErrorJava

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 3 column 16 path $.customer

What it means

Gson wanted a JSON object and found something else. Read the end of the message. column 1 path $ means the body is not JSON. A path like $.customer means one field has the wrong type in your class.

Last verified on Gson 2.13.

This JSON is valid. The problem is on the Java side, so a JSON validator would say it is fine.

Usually
line 1 column 1 path $: the body is not JSONbut was STRING at column 2 path $: the body is a JSON stringpath $.customer: one field has the wrong typeBEGIN_ARRAY: a list where the class wants one object, or the reverseIt happens in Retrofit too
Fix
Read the column and the path, then fix what they point at.

The fix

Read the column and the path, then fix what they point at. If the body is not JSON, log it and find out why you got it. If a field has the wrong type, change the field in your class, as in the fixed code.

The JSON that was sent: request.jsonValid JSON
{
  "id": 1,
  "customer": "Ada"
}
A JSON validator says this is fine.
Broken.javaThrows
public class Order {
  public long id;
  public Customer customer;
}

public class Customer {
  public String name;
}

Order order = gson.fromJson(json, Order.class);
Throws JsonSyntaxException

5 lines change.

Fixed.javaWorks
public class Order {
  public long id;
  public String customer;
}

Order order = gson.fromJson(json, Order.class);
Reads without an error.

How the message appears

The first line is the example on this page. The rest are the same message for other bodies: an HTML page or plain text, a JSON string, a list where the class wants one object, an object where it wants a list, and the reverse of this page, an object in a field that is a string. Every one of them is followed by the link on the last line.

Console
  • com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 3 column 16 path $.customer
  • com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
  • com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 2 path $
  • com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
  • com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
  • com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 22 path $.status
  • See https://github.com/google/gson/blob/main/Troubleshooting.md#unexpected-json-structure

What causes it

  1. line 1 column 1 path $: the body is not JSON

    Column 1 and path $ mean the first character of the body is not JSON. It is an HTML error page or a plain-text error such as error: upstream timeout. Gson reads leniently by default, so it takes that text as a string and says your class needs an object. A strict Gson reader calls the same kind of text malformed JSON instead. Log the body and look at the status code.

  2. but was STRING at column 2 path $: the body is a JSON string

    The whole body is one JSON string, and the object you wanted is inside it with every quote escaped. That means the JSON was encoded twice. The sender turned an object into a string and then put the string through JSON again. Fix the sender so it sends the object.

  3. path $.customer: one field has the wrong type

    A name after the $ is the field. In this example the JSON has "customer": "Ada", a string, and your class has public Customer customer, so Gson wants an object there. Change the field to String, as in the fixed code, or get the sender to send an object. The reverse gives Expected a string but was BEGIN_OBJECT with the path of the field.

  4. BEGIN_ARRAY: a list where the class wants one object, or the reverse

    If the JSON starts with [ and you asked for Order.class, Gson says Expected BEGIN_OBJECT but was BEGIN_ARRAY. If it starts with { and you asked for a List<Order>, it says Expected BEGIN_ARRAY but was BEGIN_OBJECT. Look at the first character of the body and at the type you asked for.

  5. It happens in Retrofit too

    Retrofit with the Gson converter hands the response body to Gson, so a Retrofit call can throw this as well. The cause is the same. Log the raw body and compare it with your class. Retrofit was not tested here.

Worth knowing

NoteJackson words a list read into one object as Cannot deserialize value of type. See Cannot deserialize from Array value.

NoteWhen the text is not JSON at all and you read it as a plain Object, Gson says MalformedJsonException instead. See MalformedJsonException.

Your JSON and text are processed entirely in your browser and are never sent anywhere. To understand how the site is used we record each page view (the page, the referrer and an approximate location from your IP address) and keep it for 90 days. Nothing is stored on your device unless you choose it. Accept to also store a device identifier here and record your browser and device characteristics, which is what lets a return visit be told from a new one. Decline and nothing is sent at all. We respect Do Not Track. See our privacy policy.