{"record":{"id":"427ef0dd2bd1f112","repo":"elastic/elasticsearch","slug":"cannot-expand-because-is-not-an-object","errorCode":null,"errorMessage":"cannot expand [{}], because [{}] is not an object field, but a value field","messagePattern":"cannot expand \\[(.+?)\\], because \\[(.+?)\\] is not an object field, but a value field","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DotExpanderProcessor.java","lineNumber":86,"sourceCode":"        if (map.containsKey(fieldName)) {\n            if (ingestDocument.hasField(pathToExpand)) {\n                Object value = map.remove(fieldName);\n                if (override) {\n                    ingestDocument.setFieldValue(pathToExpand, value);\n                } else {\n                    ingestDocument.appendFieldValue(pathToExpand, value);\n                }\n            } else {\n                // check whether we actually can expand the field in question into an object field.\n                // part of the path may already exist and if part of it would be a value field (string, integer etc.)\n                // then we can't override it with an object field and we should fail with a good reason.\n                // IngestDocument#setFieldValue(...) would fail too, but the error isn't very understandable\n                for (int index = pathToExpand.indexOf('.'); index != -1; index = pathToExpand.indexOf('.', index + 1)) {\n                    String partialPath = pathToExpand.substring(0, index);\n                    if (ingestDocument.hasField(partialPath)) {\n                        Object val = ingestDocument.getFieldValue(partialPath, Object.class);\n                        if ((val instanceof Map) == false) {\n                            throw new IllegalArgumentException(\n                                \"cannot expand [\"\n                                    + pathToExpand\n                                    + \"], because [\"\n                                    + partialPath\n                                    + \"] is not an object field, but a value field\"\n                            );\n                        }\n                    } else {\n                        break;\n                    }\n                }\n                Object value = map.remove(fieldName);\n                ingestDocument.setFieldValue(pathToExpand, value);\n            }\n        }\n    }\n\n    @Override","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DotExpanderProcessor.java#L68-L104","documentation":"Thrown by DotExpanderProcessor.execute when expanding a dotted field name into nested objects would require converting an existing leaf value into a map. The processor walks each dotted prefix and verifies that any existing value at that prefix is a Map; if it finds a scalar (string, number, etc.) it aborts, because overwriting a leaf with an object would silently destroy data. The error names both the full path and the conflicting partial path.","triggerScenarios":"A document where 'a' already exists as a string and the dot_expander is asked to expand 'a.b'. The partial path 'a' is a value field, not a map, so expansion of 'a.b' is rejected.","commonSituations":"Heterogeneous documents where the same key is sometimes a scalar and sometimes a parent object; field collisions after rename or enrichment processors; producing systems that flatten some keys but not others; JSON with inconsistent nesting.","solutions":["Rename or remove the conflicting scalar field (or its parent) before the dot_expander processor runs.","Restructure the producer so the field is consistently an object.","Use a 'convert' or 'script' processor to coerce the scalar into a map shape before expansion, or use 'override' carefully.","Quarantine the document via on_failure if it is genuinely ambiguous."],"exampleFix":"// before - document has a=\"hello\" and dot_expander expands a.b=\"x\"\n{\"dot_expander\": {\"field\": \"a.b\"}}\n// after - rename the conflicting scalar first\n{\"rename\": {\"field\": \"a\", \"target_field\": \"a_value\", \"ignore_missing\": true}},\n{\"dot_expander\": {\"field\": \"a.b\"}}","handlingStrategy":"type-guard","validationCode":"// Guard before dot_expander: skip if any prefix would collide with a scalar.\n{\"script\": {\"source\": \"def p = 'a'; if (ctx.containsKey(p) && !(ctx[p] instanceof Map)) { ctx.a_value = ctx.a; ctx.remove('a'); }\"}}","typeGuard":"// painless\nboolean prefix_is_map_or_absent(def doc, String prefix) { return doc[prefix] == null || doc[prefix] instanceof Map; }","tryCatchPattern":"{\"on_failure\": [{\"index\": {\"index\": \"ingest-dlq\"}}]}","preventionTips":["Rename conflicting scalar fields before dot_expander.","Make producers consistent: a key is either always a scalar or always a parent object.","Inspect documents that mix types in CI before deploying the pipeline."],"tags":["ingest","dot-expander","object-expansion","data-quality"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}