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

Logstash Error Guide: 'version conflict, document already exists (409)' — Fix Duplicate document_id Writes

Quick answer

Fix Logstash Elasticsearch output 'version conflict, document already exists (409)': fix document_id, choose create vs index, and use idempotent writes.

  • #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

When the Logstash elasticsearch output uses action => "create" with an explicit document_id, Elasticsearch refuses to overwrite an existing document and returns HTTP 409, which Logstash logs as:

[WARN ][logstash.outputs.elasticsearch] Failed action
{:status=>409, :action=>["create", {:_id=>"order-10482",
:_index=>"orders-2026.07.10"}, #<LogStash::Event>],
:response=>{"create"=>{"status"=>409, "error"=>{
"type"=>"version_conflict_engine_exception",
"reason"=>"[order-10482]: version conflict, document already exists (current version [1])"}}}}

version conflict, document already exists (409) means a document with that _id is already indexed and the create action will not replace it. Whether this is a harmless dedupe or real data loss depends entirely on your intent — it is often expected, but a mis-set document_id can silently drop updates.

Symptoms

  • status=>409 with version_conflict_engine_exception ... document already exists in logstash-plain.log.
  • Events appear “missing” in Elasticsearch even though Logstash processed them (they were rejected as duplicates).
  • The rate of 409s spikes after enabling action => "create" or setting a non-unique document_id.
  • Reprocessing the same input (a replayed persistent queue, a re-read file) produces a burst of 409s.
  • With concurrent updates to the same _id, occasional 409s from optimistic-concurrency version checks.

Common Root Causes

  • Non-unique document_id — the id template collapses distinct events to the same _id (e.g. a coarse timestamp or a repeated key).
  • action => "create" when you meant indexcreate refuses to overwrite; index upserts. Choosing the wrong one turns updates into 409s.
  • Reprocessing the same data — a replayed queue, a re-tailed file, or a Beats resend re-emits events that already exist.
  • Intentional dedup working as designed — you set a stable document_id specifically to drop duplicates, and 409s are the expected mechanism.
  • Concurrent writers — two pipelines/instances writing the same _id hit optimistic-concurrency conflicts.
  • External versioning misuseversion/version_type => external with an out-of-order version number.

Diagnostic Workflow

Inspect the output’s id and action settings — the two variables that produce 409s:

output {
  elasticsearch {
    hosts => ["http://es:9200"]
    index => "orders-%{+YYYY.MM.dd}"
    document_id => "%{order_id}"   # must be UNIQUE per distinct document
    action => "create"            # "create" = fail if exists; "index" = overwrite/upsert
  }
}

Check whether the id is actually unique by sampling the field in your events:

grep -oE '"order_id":"[^"]+"' /var/log/app/orders.log | sort | uniq -d | head

Confirm the conflicting document already exists in Elasticsearch and compare it to the incoming event:

curl -s 'http://es:9200/orders-2026.07.10/_doc/order-10482?pretty'
curl -s 'http://es:9200/orders-2026.07.10/_count?q=order_id:10482'

Look at the scale of 409s in the pipeline stats vs successful writes:

curl -s localhost:9600/_node/stats/pipelines?pretty | grep -A8 elasticsearch
grep -c 'status=>409' /var/log/logstash/logstash-plain.log

If updates should overwrite, switch the action (in the .conf) and reload:

    action => "index"     # upsert: last write wins, no 409 on existing _id
    doc_as_upsert => true # with action => "update", create-or-update semantics
kill -SIGHUP $(pgrep -f org.logstash.Logstash)

Example Root Cause Analysis

A pipeline indexed order events with document_id => "%{order_id}" and action => "create", intending each order to appear once. Business logic then began emitting an updated event when an order shipped — same order_id, new status. Every shipment update hit version conflict, document already exists (409) and was dropped, so Kibana never showed shipped orders as shipped.

curl .../orders-.../_doc/order-10482 confirmed the stored document still had status: "placed" while the log showed a 409 for the shipping update. The root cause was semantic: create is insert-only, but the workflow required updates. Changing action => "index" (upsert, last-write-wins) let the shipping event overwrite the placed document. The 409s vanished and order statuses updated correctly. Where they genuinely wanted insert-only dedup on a different pipeline, they kept create and downgraded the 409 log noise by expecting it.

Prevention Best Practices

  • Choose the action to match intent: create for strict insert-only dedup, index for upsert/overwrite, update + doc_as_upsert for partial updates.
  • Build document_id from a genuinely unique key per distinct document; verify uniqueness before deploying (a uniq -d check catches collisions).
  • Treat expected 409s from intentional dedup as informational, not errors — filter them from alerting so real problems stand out.
  • When reprocessing data, decide up front whether re-writes should overwrite (index) or be skipped (create) and set the action accordingly.
  • Avoid two pipelines/instances writing the same _id concurrently; partition ownership or use a single writer per document space.
  • If using external versioning, ensure version numbers are monotonic and correctly ordered to prevent spurious conflicts.

Quick Command Reference

# What id/action is the output using?
grep -E 'document_id|action' /etc/logstash/conf.d/*.conf

# Is the document_id field actually unique?
grep -oE '"order_id":"[^"]+"' /var/log/app/orders.log | sort | uniq -d | head

# Does the conflicting doc already exist?
curl -s 'http://es:9200/orders-2026.07.10/_doc/order-10482?pretty'

# How many 409s vs successes?
grep -c 'status=>409' /var/log/logstash/logstash-plain.log
curl -s localhost:9600/_node/stats/pipelines?pretty | grep -A8 elasticsearch

# Switch to upsert semantics and reload
kill -SIGHUP $(pgrep -f org.logstash.Logstash)

Conclusion

version conflict, document already exists (409) from the Logstash elasticsearch output means a document with that _id already exists and action => "create" will not overwrite it. Decide what you actually want: strict insert-only dedup (keep create and treat 409s as expected), or updates (switch to action => "index" upsert, or update with doc_as_upsert). Verify your document_id is truly unique per distinct document, and filter intentional-dedup 409s out of alerting so genuine conflicts — like dropped updates from a wrong action — are visible immediately.

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.