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

  1. 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.
  2. 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

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

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/e91a3d8de19a2165. Report an issue: GitHub.