elastic/elasticsearch · error · IllegalArgumentException

field [{}] of type [{}] cannot be cast to a list or map

Error message

field [{}] of type [{}] cannot be cast to a list or map

What it means

Thrown by ForEachProcessor.execute (sync path) when the field value is neither a Map nor a List. foreach can only iterate maps and lists; any other type (number, string, boolean, object) is rejected with the actual Java type name embedded in the message. IllegalArgumentException surfacing a schema/type mismatch between pipeline expectation and document content.

Source

Thrown at modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ForEachProcessor.java:76

    }

    @Override
    public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
        assert isAsync() == false;

        Object o = ingestDocument.getFieldValue(field, Object.class, ignoreMissing);
        if (o == null) {
            if (ignoreMissing) {
                return ingestDocument;
            } else {
                throw new IllegalArgumentException("field [" + field + "] is null, cannot loop over its elements.");
            }
        } else if (o instanceof Map<?, ?> map) {
            return iterateMap(ingestDocument, map);
        } else if (o instanceof List<?> list) {
            return iterateList(ingestDocument, list);
        } else {
            throw new IllegalArgumentException(
                "field [" + field + "] of type [" + o.getClass().getName() + "] cannot be cast to a " + "list or map"
            );
        }
    }

    private IngestDocument iterateMap(IngestDocument document, Map<?, ?> map) throws Exception {
        var newValues = Maps.newHashMapWithExpectedSize(map.size());
        for (Map.Entry<?, ?> e : map.entrySet()) {
            String key = (String) e.getKey();
            Object previousKey = document.getIngestMetadata().put("_key", key);
            Object value = e.getValue();
            Object previousValue = document.getIngestMetadata().put("_value", value);
            try {
                processor.execute(document);
            } finally {
                String newKey = (String) document.getIngestMetadata().get("_key");
                if (Strings.hasText(newKey)) {
                    newValues.put(newKey, document.getIngestMetadata().put("_value", previousValue));

View on GitHub (pinned to db6a809a66)

Solutions

  1. Add a convert or split processor upstream to coerce the field into a list or map before foreach.
  2. Fix the source data to send the field as an array/object.
  3. Point the foreach field at the correct field path that actually holds a collection.

Example fix

// before
{"foreach": {"field": "tags", "processor": {...}}}
// after
{"split": {"field": "tags", "separator": ","}},
{"foreach": {"field": "tags", "processor": {...}}}
Defensive patterns

Strategy: type-guard

Validate before calling

Object v = doc.getFieldValue(field, Object.class, true);
if (v != null && !(v instanceof Map) && !(v instanceof List)) {
    throw new IllegalStateException("foreach field " + field + " is " + v.getClass().getName());
}

Type guard

static boolean isIterable(Object v) {
    return v == null || v instanceof Map || v instanceof List;
}

Try / catch

try {
    processor.execute(doc);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("cannot be cast to a list or map")) {
        // coerce or route to failure store
    } else throw e;
}

Prevention

When it happens

Trigger: The configured field exists and is non-null but holds a scalar (e.g. a String or Integer) where the pipeline assumed a list/map. E.g. field "tags" is "a,b,c" string instead of ["a","b","c"].

Common situations: Source data changed shape (array became scalar), upstream convert/split processor missing, or mapping conflict where the same field is indexed as both keyword and object elsewhere.

Related errors


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