Logstash Error Guide: 'version conflict, document already exists (409)' — Fix Duplicate document_id Writes
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
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=>409withversion_conflict_engine_exception ... document already existsinlogstash-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-uniquedocument_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 meantindex—createrefuses to overwrite;indexupserts. 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_idspecifically to drop duplicates, and 409s are the expected mechanism. - Concurrent writers — two pipelines/instances writing the same
_idhit optimistic-concurrency conflicts. - External versioning misuse —
version/version_type => externalwith 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:
createfor strict insert-only dedup,indexfor upsert/overwrite,update+doc_as_upsertfor partial updates. - Build
document_idfrom a genuinely unique key per distinct document; verify uniqueness before deploying (auniq -dcheck 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
_idconcurrently; 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.
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.