elastic/elasticsearch · error · IllegalArgumentException

cannot add non-map fields to root of document

Error message

cannot add non-map fields to root of document

What it means

Thrown by JsonProcessor.mergeParsedJson when the parsed JSON value is not a Map (i.e. it was a scalar, array, or null after parsing). The json processor can only merge object-shaped JSON into the document root; non-map values are rejected. IllegalArgumentException guarding document-root integrity.

Source

Thrown at modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java:165

        boolean allowDuplicateKeys,
        ConflictStrategy conflictStrategy,
        boolean strictJsonParsing
    ) {
        Object value = apply(ctx.get(fieldName), allowDuplicateKeys, strictJsonParsing);
        mergeParsedJson(ctx, value, conflictStrategy);
    }

    private static void mergeParsedJson(Map<String, Object> ctx, Object value, ConflictStrategy conflictStrategy) {
        if (value instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> map = (Map<String, Object>) value;
            if (conflictStrategy == ConflictStrategy.MERGE) {
                recursiveMerge(ctx, map);
            } else {
                ctx.putAll(map);
            }
        } else {
            throw new IllegalArgumentException("cannot add non-map fields to root of document");
        }
    }

    public static void recursiveMerge(Map<String, Object> target, Map<String, Object> from) {
        for (String key : from.keySet()) {
            if (target.containsKey(key)) {
                Object targetValue = target.get(key);
                Object fromValue = from.get(key);
                if (targetValue instanceof Map && fromValue instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> targetMap = (Map<String, Object>) targetValue;
                    @SuppressWarnings("unchecked")
                    Map<String, Object> fromMap = (Map<String, Object>) fromValue;
                    recursiveMerge(targetMap, fromMap);
                } else {
                    target.put(key, fromValue);
                }
            } else {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Ensure the source field contains a JSON object (curly-brace) when merging into the document root.
  2. If a scalar/array is valid, set target_field to a named sub-field instead of root so it becomes the field value.
  3. Pre-validate or filter documents whose JSON is non-object before the json processor.

Example fix

// before
{"json": {"field": "raw", "target_field": "_source"}}
// raw = '[1,2,3]'
// after
{"json": {"field": "raw", "target_field": "parsed_array"}}
Defensive patterns

Strategy: type-guard

Validate before calling

Object parsed = JsonProcessor.apply(rawValue, allowDup, strict);
if (!(parsed instanceof Map)) {
    // route scalar/array values to a sub-field instead of root
}

Type guard

static boolean isMergeableToRoot(Object v) {
    return v instanceof Map;
}

Try / catch

try {
    JsonProcessor.apply(ctx, fieldName, allowDup, conflictStrategy, strict);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("cannot add non-map fields to root of document")) {
        // re-run with a non-root target_field
    } else throw e;
}

Prevention

When it happens

Trigger: The target_field points to the document root (or merge target is ctx itself) and the parsed JSON value is a scalar, array, or null — e.g. field contains "42" or "[1,2,3]" instead of "{...}".

Common situations: Producer emits a JSON scalar or array where an object was expected; misconfigured target_field pointing at root instead of a sub-field; or non-object JSON payloads.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/29d8d13ded5f327e. Report an issue: GitHub.