hibernate/hibernate-orm · error · IllegalArgumentException
Malformed JSON. Expected object but got: " + event
Error message
Malformed JSON. Expected object but got: " + event
What it means
JsonHelper.deserialize expects the top-level element of a JSON-mapped column to be an object (the representation of an embeddable). If the reader yields anything else - an array, string, number, boolean - it throws IllegalArgumentException('Malformed JSON. Expected object but got: <event>'). The document is valid JSON but has the wrong top-level shape for the mapping.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java:229
/**
* Deserialize a JSON value to Java Object
* @param embeddableMappingType the mapping type
* @param reader the JSON reader
* @param returnEmbeddable do we return an Embeddable object or array of Objects
* @param options wrappping options
* @return the deserialized value
*/
public static <X> X deserialize(
EmbeddableMappingType embeddableMappingType,
JsonDocumentReader reader,
boolean returnEmbeddable,
WrapperOptions options) throws SQLException {
final JsonDocumentItemType event;
if ( !reader.hasNext() || ( event = reader.next() ) == JsonDocumentItemType.NULL_VALUE ) {
return null;
}
if ( event != JsonDocumentItemType.OBJECT_START ) {
throw new IllegalArgumentException("Malformed JSON. Expected object but got: " + event);
}
final X result = consumeJsonDocumentItems( reader, embeddableMappingType, returnEmbeddable, options );
assert !reader.hasNext();
return result;
}
// This is also used by Hibernate Reactive
public static <X> X arrayFromString(
JavaType<X> javaType,
JdbcType elementJdbcType,
String string,
WrapperOptions options) throws SQLException {
if ( string == null ) {
return null;
}
return deserializeArray( javaType, elementJdbcType, new StringJsonDocumentReader( string ), options );
}View on GitHub (pinned to fad1729dce)
Solutions
- If the data really is an array, remap the attribute as a plural JSON type (@JdbcTypeCode(SqlTypes.JSON) on List<T>) so the array path (deserializeArray) is used.
- If the mapping should stay an embeddable, migrate the data to wrap the value in an object (e.g., {"items": [...]}) or replace arrays with objects.
Example fix
// before: column holds ["a","b"] but mapping expects an embeddable object
@JdbcTypeCode(SqlTypes.JSON)
@Embedded Attributes attrs; // load -> Malformed JSON. Expected object but got: ARRAY_START
// after: map the plural data as a list
@JdbcTypeCode(SqlTypes.JSON)
List<String> attrs;
// or wrap in an object and keep an @Embeddable Attributes { List<String> items; } Defensive patterns
Strategy: validation
Validate before calling
// Validate the top-level shape before the entity load (Jackson example)
static void assertTopLevelObject(String json) {
com.fasterxml.jackson.databind.JsonNode n;
try { n = new com.fasterxml.jackson.databind.ObjectMapper().readTree(json); }
catch (Exception e) { throw new IllegalArgumentException("Invalid JSON", e); }
if (!n.isObject()) {
throw new IllegalArgumentException("Expected a JSON object for embeddable mapping, got: " + n.getNodeType());
}
} Try / catch
try {
return session.find(Product.class, id);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Malformed JSON. Expected object")) {
// column holds an array/scalar where the embeddable needs an object: fix mapping or data
} else {
throw e;
}
} Prevention
- Keep singular/plural JSON attributes consistent with the stored shape across refactors
- Add migration checks that verify top-level JSON shape per column
- Write JSON columns only through the owning application
When it happens
Trigger: Loading an entity whose json aggregate column contains an array or a scalar instead of an object - e.g., the attribute was changed from List<Embeddable> to a single Embeddable (or data was written by another writer) without migrating the column.
Common situations: Changing an attribute between a plural JSON type and a single aggregate embeddable; other applications seeding the column with arrays; test fixtures inserting raw scalars.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Can't parse JSON object for selectable [%s] which is not of
- Expected JSON object end, but none found.
- Malformed JSON. Expected array but got: " + event
- Could not find selectable [%s] in embeddable type [%s] for J
- Can't parse JSON array for selectable [%s] which is not of t
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e91a3d8de19a2165.
Report an issue: GitHub.