Cannot deserialize value of type from Object value
Updated by John Marshall
Jackson parsed the JSON, but a value has a different shape from the Java type it’s going into. The message names the type you asked for and what the JSON had instead: “from Object value” means a { } object, and “from Array value” means a [ ] list. Most often the whole response is shaped differently from what your code expects, such as a list that comes wrapped inside an object.
Quick fix
- Read the type in backticks and the kind of value after “from”. The reference chain at the end, if there is one, names the field.
- Look at the actual JSON at that spot. Paste the response into the JSON viewer to see its structure.
- Change your Java type to match: a wrapper class for a list inside an object, a
Listfor an array, or a nested class for an object.
An example
An API returns a page of users, and the code reads it as a list:
// The API's response
{"data": [{"firstName": "Maya", "lastName": "Chen"}], "page": 1}
// The code
List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {});Cannot deserialize value of type `java.util.ArrayList<com.example.User>` from Object value (token `JsonToken.START_OBJECT`)
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]The code asked for a list, but the response starts with {, so Jackson stops at line 1, column 1. The users are one level down, in data.
What each version means
Each message starts with Cannot deserialize value of type. The rest tells you which case you have:
`java.util.ArrayList<…User>` from Object value (token `JsonToken.START_OBJECT`) | You asked for a list, and the JSON is an object. Usually the list is inside it, like {"data": […]}. Fix |
`com.example.User` from Array value (token `JsonToken.START_ARRAY`) | You asked for one object, and the JSON is a list of them. Fix |
`java.lang.String` from Object value (token `JsonToken.START_OBJECT`) | A field you declared as a String holds an object, like {"given": "Maya"}. Make the field a class with matching fields. |
`java.lang.String` from Array value (token `JsonToken.START_ARRAY`) | A String field holds a list. Make it a List<String>. |
`int` from String "abc": not a valid `int` value | The field is a number and the JSON has text that isn’t one. Fix |
`com.example.Status` from String "shipped": not one of the values accepted for Enum class: [SHIPPED, PENDING] | The JSON spells an enum value differently from your enum. Fix |
When the problem is inside the document rather than at the top, the message ends with a reference chain such as (through reference chain: com.example.User["firstName"]), naming the field that didn’t fit.
A list wrapped in an object
Many APIs put the list inside an object with paging details next to it. Write a small class for the outer object and take the list from it:
public class UserPage {
public List<User> data;
public int page;
}
List<User> users = mapper.readValue(json, UserPage.class).data;If you only need the list and don’t want another class, read the response as a tree and convert the part you need:
JsonNode root = mapper.readTree(json);
List<User> users = mapper.convertValue(root.get("data"), new TypeReference<List<User>>() {});A list where you expected one object
The opposite case, from Array value, means the JSON starts with [. Even a search that finds one match usually comes back as a list of one. Read it as a list:
List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {});
// or
User[] users = mapper.readValue(json, User[].class);Use a TypeReference or an array type, not List.class. With List.class, Jackson doesn’t know the items are users, so it fills the list with LinkedHashMap objects. That works until the first users.get(0), which fails with a ClassCastException.
Numbers that aren’t numbers
Jackson 2 is lenient with numbers. It reads "34" in quotes into an int without complaint, so it only fails when the text isn’t a number at all, like "abc" or "N/A". If the API sends placeholders like that, make the field a String and convert it yourself.
Two of its defaults hide problems instead of reporting them. A null for an int becomes 0, and 34.5 becomes 34, with the decimal dropped. Use Integer if the value can be missing, and double or BigDecimal if it can have a fraction. To get an error for both instead, turn them off:
ObjectMapper mapper = JsonMapper.builder()
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
.disable(DeserializationFeature.ACCEPT_FLOAT_AS_INT)
.build();Jackson 3 already fails on null for primitives by default. A value too big for the type has its own message, Numeric value (3000000000) out of range of int, and the fix is long.
Enum values
Jackson matches enum values by their exact Java name, so "shipped" doesn’t match SHIPPED. The message lists the values it would have accepted. Either accept any case, or spell out each value:
// Jackson 2: accept "shipped" as well as "SHIPPED", for every enum
ObjectMapper mapper = JsonMapper.builder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.build();
// Or name each value the way the API spells it
public enum Status {
@JsonProperty("pending") PENDING,
@JsonProperty("shipped") SHIPPED
}If the API might add values later, DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL reads anything unrecognized as null instead of failing. In Jackson 3, that setting moved to EnumFeature. The @JsonProperty approach works the same in both versions.
LocalDate and other java.time types
Date fields fail with a different message in Jackson 2:
Java 8 date/time type `java.time.LocalDate` not supported by default:
add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handlingJackson 2 needs a separate module for LocalDate, Instant and the other java.time types. Add it and register it:
// Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
ObjectMapper mapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.build();Spring Boot registers this module for you, and Jackson 3 has it built in. If you see the message in a Spring app, look for code that creates its own new ObjectMapper().
The same error elsewhere
| Jackson 2.12 and later | Cannot deserialize value of type `java.util.ArrayList<com.example.User>` from Object value (token `JsonToken.START_OBJECT`) |
|---|---|
| Jackson before 2.12 | Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token |
| Spring MVC | JSON parse error: Cannot deserialize value of type `java.util.ArrayList<com.example.User>` from Object value |
| Go | json: cannot unmarshal object into Go value of type []main.User |
| C#, System.Text.Json | The JSON value could not be converted to System.Collections.Generic.List`1[MyApp.User]. Path: $ |