apache/flink · error · java.lang.IllegalStateException
Illegal JSON object data...
Error message
Illegal JSON object data...
What it means
IllegalStateException from createRowConverter's lambda when the root token is not START_OBJECT — the JSON record for a ROW-typed schema must be a JSON object '{...}', but the payload at that position is a scalar, array, or the stream is malformed at that point. This guards every top-level row and nested ROW conversion.
Source
Thrown at flink-formats/flink-json/src/main/java/org/apache/flink/formats/json/JsonParserToRowDataConverters.java:407
};
}
public JsonParserToRowDataConverter createRowConverter(RowType rowType) {
final JsonParserToRowDataConverter[] fieldConverters =
rowType.getFields().stream()
.map(RowType.RowField::getType)
.map(this::createConverter)
.toArray(JsonParserToRowDataConverter[]::new);
final String[] fieldNames = rowType.getFieldNames().toArray(new String[0]);
Map<String, Integer> nameIdxMap = new HashMap<>();
for (int i = 0; i < rowType.getFieldCount(); i++) {
nameIdxMap.put(fieldNames[i], i);
}
return jp -> {
if (jp.currentToken() != JsonToken.START_OBJECT) {
throw new IllegalStateException("Illegal JSON object data...");
}
int arity = nameIdxMap.size();
GenericRowData row = new GenericRowData(arity);
int cnt = 0;
jp.nextToken();
while (jp.currentToken() != JsonToken.END_OBJECT) {
if (cnt >= arity) {
skipToNextField(jp);
continue;
}
String fieldName = jp.getText();
jp.nextToken();
Integer idx = nameIdxMap.get(fieldName);
if (idx != null) {
try {
Object convertField = fieldConverters[idx].convert(jp);
row.setField(idx, convertField);
} catch (Throwable t) {View on GitHub (pinned to 2f3c205e92)
Solutions
- Ensure each record is a complete JSON object matching the DDL columns
- Filter/quarantine non-object messages at the source (e.g., a preceding filter or topic separation)
- With 'json.ignore-parse-errors'='true' such records are skipped instead of failing the job
Example fix
// producer: before
producer.send(new ProducerRecord<>(topic, "42"));
// after
producer.send(new ProducerRecord<>(topic, "{\"value\": 42}")); Defensive patterns
Strategy: validation
Validate before calling
JsonNode n = mapper.readTree(sample); // root must be an object for row schemas
if (!n.isObject()) throw new IllegalStateException("each record must be a JSON object"); Try / catch
catch (IllegalStateException e) {
if (e.getMessage().contains("Illegal JSON object data")) {
// non-object record hit a ROW schema; filter or fix producer
}
} Prevention
- Separate control/heartbeat messages from data records at the source
- Ensure encoders always emit objects for row-typed records
When it happens
Trigger: Kafka message contains a bare number/string ('42', '"ok"') instead of an object; a JSON array element is not an object when converting nested ROWs; trailing garbage making the token stream diverge.
Common situations: Topics mixing control/heartbeat messages with data; producers sending JSON-encoded scalars for empty payloads; newline-delimited JSON split incorrectly.
Related errors
- Illegal JSON array data...
- Illegal JSON map data...
- Illegal Json Data...
- Unknown field name '%s' for mapping to a position.
- Unknown field name '%s' for mapping to a row position. Avail
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/8ed0d44259e13e8d.
Report an issue: GitHub.