apache/flink · error · java.lang.IllegalStateException

Illegal JSON map data...

Error message

Illegal JSON map data...

What it means

IllegalStateException from createMapConverter's lambda when the current token is not START_OBJECT while converting a MAP column — the JSON value at that position is a scalar or array where '{' ... '}' (a JSON object) is expected. Like the array variant, it is a payload-vs-schema shape mismatch.

Source

Thrown at flink-formats/flink-json/src/main/java/org/apache/flink/formats/json/JsonParserToRowDataConverters.java:379

            final Object[] array = (Object[]) Array.newInstance(elementClass, result.size());
            return new GenericArrayData(result.toArray(array));
        };
    }

    private JsonParserToRowDataConverter createMapConverter(
            String typeSummary, LogicalType keyType, LogicalType valueType) {
        if (!keyType.is(LogicalTypeFamily.CHARACTER_STRING)) {
            throw new UnsupportedOperationException(
                    "JSON format doesn't support non-string as key type of map. "
                            + "The type is: "
                            + typeSummary);
        }
        final JsonParserToRowDataConverter keyConverter = createConverter(keyType);
        final JsonParserToRowDataConverter valueConverter = createConverter(valueType);

        return jp -> {
            if (jp.currentToken() != JsonToken.START_OBJECT) {
                throw new IllegalStateException("Illegal JSON map data...");
            }
            Map<Object, Object> result = new HashMap<>();
            while (jp.nextToken() != JsonToken.END_OBJECT) {
                Object key = keyConverter.convert(jp);
                jp.nextToken();
                Object value = valueConverter.convert(jp);
                result.put(key, value);
            }
            return new GenericMapData(result);
        };
    }

    public JsonParserToRowDataConverter createRowConverter(RowType rowType) {
        final JsonParserToRowDataConverter[] fieldConverters =
                rowType.getFields().stream()
                        .map(RowType.RowField::getType)
                        .map(this::createConverter)
                        .toArray(JsonParserToRowDataConverter[]::new);

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Match the DDL to the real payload: ARRAY<STRING> / ARRAY<ROW<k STRING,v STRING>> for pair-lists, MAP<STRING,T> only for genuine JSON objects
  2. Fix the producer to emit a JSON object for map fields
  3. Validate a sample of the real data against the schema before deploying

Example fix

-- before
attrs MAP<STRING, INT>   -- data is "attrs": [{"k":"a","v":1}]

-- after
attrs ARRAY<ROW<k STRING, v INT>>
Defensive patterns

Strategy: validation

Validate before calling

JsonNode n = mapper.readTree(sample).get("attrs");
if (!n.isObject()) throw new IllegalStateException("map field must be a JSON object");

Try / catch

catch (IllegalStateException e) {
    if (e.getMessage().contains("Illegal JSON map data")) {
        // schema says MAP but data is not an object; fix DDL or producer
    }
}

Prevention

When it happens

Trigger: Schema declares MAP<STRING, T> but the payload has "attrs": ["a","b"] or "attrs": 42; nested map where the value converter expects an object and finds another token.

Common situations: Producers emitting arrays of key-value pairs instead of objects; schema evolution between list and object forms; DDL written from documentation rather than real samples.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/b9b53d00015ca979. Report an issue: GitHub.