Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Logstash By James Joyner IV · · 9 min read

Logstash Error Guide: 'Could not index event to Elasticsearch ... mapper_parsing_exception' — Fix the Type Conflict

Quick answer

Fix Logstash 'Could not index event to Elasticsearch ... mapper_parsing_exception': coerce field types, control dynamic mapping, and capture rejects in the DLQ.

  • #logstash
  • #logging
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

Elasticsearch enforces a mapping per index: once a field has a type, every later document must supply a value that fits that type. When Logstash sends an event whose field doesn’t match the established mapping, the elasticsearch output logs the per-document rejection and moves on:

[WARN ][logstash.outputs.elasticsearch] Could not index event to Elasticsearch.
{:status=>400, :action=>["index", {:_id=>nil, :_index=>"app-logs-2026.07.11",
:routing=>nil}, {...}], :response=>{"index"=>{"error"=>{
"type"=>"mapper_parsing_exception",
"reason"=>"failed to parse field [response_time] of type [long] in document with id 'abc123'.
Preview of field's value: '12.5ms'",
"caused_by"=>{"type"=>"illegal_argument_exception",
"reason"=>"For input string: \"12.5ms\""}}}}}

This is a non-retryable 400: retrying the same document produces the same error forever, so the event is dropped (or sent to the dead letter queue if enabled). The reason names the exact field, its mapped type, and a preview of the offending value — that line is the whole diagnosis.

Symptoms

  • Could not index event to Elasticsearch with "type"=>"mapper_parsing_exception" and :status=>400 in logstash-plain.log.
  • The reason names a specific field and a type mismatch (a string where a long/date/ip is mapped, an object where a scalar is mapped, or vice versa).
  • Events from one log source index fine while a different source silently disappears from Kibana.
  • The DLQ grows steadily (if enabled) or event counts don’t add up (if not).
  • Errors began right after a new log format, a code change, or a new field started flowing.

Common Root Causes

  • Type conflict across sources — the first document to create the field fixed its type; a later source sends a different type (e.g. status as 200 then "200", response_time as 12 then "12.5ms").
  • String where a number/date/ip is mapped — a unit suffix (ms, kb), a placeholder ("-", "N/A", "null"), or an unparsed value reaching a numeric/date/ip field.
  • Object vs. scalar collision — a field arrives as a plain value in some events and as a nested object in others (user = "alice" vs user = { "name": "alice" }).
  • Malformed date — a value that doesn’t match the mapped date format, when ignore_malformed is not set.
  • Dynamic mapping guessed wrong — the first-ever value (e.g. "12345") made Elasticsearch infer long, and real values are strings.

How to diagnose

Read the reason — it names the field, its mapped type, and the value that failed:

grep 'mapper_parsing_exception' /var/log/logstash/logstash-plain.log | tail -5

Confirm the field’s current mapping in Elasticsearch:

curl -s 'http://es:9200/app-logs-2026.07.11/_mapping/field/response_time?pretty'

Reproduce the rejection in isolation to see exactly what Elasticsearch dislikes:

curl -s -XPOST 'http://es:9200/app-logs-2026.07.11/_doc?pretty' \
  -H 'Content-Type: application/json' \
  -d '{"response_time":"12.5ms"}'

If the DLQ is enabled, the rejected events (and their reasons) are captured for inspection:

ls -lh /var/lib/logstash/dead_letter_queue/main/

Fixes

1. Coerce the type in the Logstash filter so Elasticsearch always sees the mapped type. Strip units and convert, in the pipeline conf:

filter {
  # "12.5ms" -> 12.5 (numeric)
  mutate { gsub => ["response_time", "ms$", ""] }
  mutate { convert => { "response_time" => "float" } }

  # Normalize placeholders to a real null before a numeric field
  if [bytes] in ["-", "N/A", "null", ""] {
    mutate { remove_field => ["bytes"] }
  } else {
    mutate { convert => { "bytes" => "integer" } }
  }
}

2. Fix the object-vs-scalar collision by renaming one shape so the two never share a field:

filter {
  if [user] and [user][name] {
    mutate { rename => { "[user][name]" => "user_name" } }
    mutate { remove_field => ["user"] }
  }
}

3. Make the mapping tolerant with an index template so a stray bad value is skipped instead of rejecting the whole document. Manage this in Elasticsearch (a composable template), not in Logstash:

PUT _index_template/app-logs
{
  "index_patterns": ["app-logs-*"],
  "template": {
    "mappings": {
      "properties": {
        "response_time": { "type": "float", "ignore_malformed": true },
        "status":        { "type": "keyword" }
      }
    }
  }
}

Note: mappings are immutable in place — changing an existing field’s type requires a rollover to a new index (the template applies to newly created indices), not an edit of the current one.

4. Capture rejects instead of dropping them by enabling the dead letter queue in logstash.yml, then reprocess after fixing the root cause:

dead_letter_queue.enable: true
path.dead_letter_queue: /var/lib/logstash/dead_letter_queue
dead_letter_queue.max_bytes: 1gb

What to watch out for

  • The second type to arrive is what gets rejected — the field’s type is fixed by whichever document created it first, so “it worked yesterday” doesn’t mean the config is right.
  • ignore_malformed silently drops the bad field’s value (the rest of the doc still indexes) — good for resilience, but you lose that datum, so alert on _ignored.
  • Enabling the DLQ without a pipeline that reads it just defers data loss until dead_letter_queue.max_bytes evicts the oldest entries.
  • Fixing the template does nothing for the existing index — new data needs a rollover or a new data-stream backing index; historical conflicts need a reindex.
  • Coerce at the source rather than raising limits or loosening mappings everywhere; a permissive keyword-everything mapping hides real data-quality problems.
Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.