{"record":{"id":"29d8d13ded5f327e","repo":"elastic/elasticsearch","slug":"cannot-add-non-map-fields-to-root-of-document","errorCode":null,"errorMessage":"cannot add non-map fields to root of document","messagePattern":"cannot add non-map fields to root of document","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java","lineNumber":165,"sourceCode":"        boolean allowDuplicateKeys,\n        ConflictStrategy conflictStrategy,\n        boolean strictJsonParsing\n    ) {\n        Object value = apply(ctx.get(fieldName), allowDuplicateKeys, strictJsonParsing);\n        mergeParsedJson(ctx, value, conflictStrategy);\n    }\n\n    private static void mergeParsedJson(Map<String, Object> ctx, Object value, ConflictStrategy conflictStrategy) {\n        if (value instanceof Map) {\n            @SuppressWarnings(\"unchecked\")\n            Map<String, Object> map = (Map<String, Object>) value;\n            if (conflictStrategy == ConflictStrategy.MERGE) {\n                recursiveMerge(ctx, map);\n            } else {\n                ctx.putAll(map);\n            }\n        } else {\n            throw new IllegalArgumentException(\"cannot add non-map fields to root of document\");\n        }\n    }\n\n    public static void recursiveMerge(Map<String, Object> target, Map<String, Object> from) {\n        for (String key : from.keySet()) {\n            if (target.containsKey(key)) {\n                Object targetValue = target.get(key);\n                Object fromValue = from.get(key);\n                if (targetValue instanceof Map && fromValue instanceof Map) {\n                    @SuppressWarnings(\"unchecked\")\n                    Map<String, Object> targetMap = (Map<String, Object>) targetValue;\n                    @SuppressWarnings(\"unchecked\")\n                    Map<String, Object> fromMap = (Map<String, Object>) fromValue;\n                    recursiveMerge(targetMap, fromMap);\n                } else {\n                    target.put(key, fromValue);\n                }\n            } else {","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java#L147-L183","documentation":"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.","triggerScenarios":"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 \"{...}\".","commonSituations":"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.","solutions":["Ensure the source field contains a JSON object (curly-brace) when merging into the document root.","If a scalar/array is valid, set target_field to a named sub-field instead of root so it becomes the field value.","Pre-validate or filter documents whose JSON is non-object before the json processor."],"exampleFix":"// before\n{\"json\": {\"field\": \"raw\", \"target_field\": \"_source\"}}\n// raw = '[1,2,3]'\n// after\n{\"json\": {\"field\": \"raw\", \"target_field\": \"parsed_array\"}}","handlingStrategy":"type-guard","validationCode":"Object parsed = JsonProcessor.apply(rawValue, allowDup, strict);\nif (!(parsed instanceof Map)) {\n    // route scalar/array values to a sub-field instead of root\n}","typeGuard":"static boolean isMergeableToRoot(Object v) {\n    return v instanceof Map;\n}","tryCatchPattern":"try {\n    JsonProcessor.apply(ctx, fieldName, allowDup, conflictStrategy, strict);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"cannot add non-map fields to root of document\")) {\n        // re-run with a non-root target_field\n    } else throw e;\n}","preventionTips":["When parsing JSON of unknown shape, target a named sub-field rather than document root.","Validate the JSON value is an object before merging into root.","Document expected JSON shape in pipeline descriptions."],"tags":["ingest","json","type-mismatch","merge"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}