Skip to content

Conflict Errors

DodaTech 2 min read

title: "Conflict Errors — Handling 409 and Duplicate Resource Issues" description: "409 Conflict errors occur when a request conflicts with the current state of the server, including duplicate resources, version conflicts, and state violations." date: 2026-06-28 lastmod: 2026-06-28 weight: 18 tags: [apis, error-handling] }

409 Conflict errors occur when a request can't be completed due to a conflict with the resource's current state, such as duplicate creation or stale version updates.

What You'll Learn

  • When to use 409 Conflict
  • Handling duplicate resources
  • Optimistic locking with version conflicts

Why It Matters

Conflict errors prevent data corruption from concurrent modifications. Clear conflict responses help clients resolve the issue automatically.

Code Examples

// Duplicate resource error
{
  "error": "CONFLICT",
  "message": "A user with this email already exists",
  "conflict_field": "email",
  "conflict_value": "alice@example.com",
  "existing_resource": "/users/42"
}

// Version conflict (optimistic locking)
{
  "error": "CONFLICT",
  "message": "Resource was modified since you last read it",
  "expected_version": 5,
  "current_version": 7,
  "suggestion": "Re-fetch the resource and retry your update"
}
# Conflict error handling
class ConflictError(Exception):
    def __init__(self, message, conflict_field=None, conflict_value=None):
        self.message = message
        self.conflict_field = conflict_field
        self.conflict_value = conflict_value

@app.route('/users', methods=['POST'])
def create_user():
    email = request.json.get('email')
    existing = db.find_user_by_email(email)
    if existing:
        raise ConflictError(
            "A user with this email already exists",
            conflict_field="email",
            conflict_value=email
        )

@app.errorhandler(ConflictError)
def handle_conflict(error):
    return jsonify({
        "error": "CONFLICT",
        "message": error.message,
        "conflict_field": error.conflict_field,
        "conflict_value": error.conflict_value
    }), 409

# Optimistic locking for updates
@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
    user = db.get_user(id)
    if user.version != request.json.get('expected_version'):
        raise ConflictError(
            "Resource modified since last read",
            conflict_field="version"
        )

Common Mistakes

1. Returning 400 Instead of 409

Duplicate resources and version conflicts are 409, not 400.

2. No Conflict Resolution Guidance

Tell clients how to resolve the conflict (re-fetch, use different value).

3. Not Including Current State

Show the current version or value so clients can decide how to proceed.

4. Race Conditions in Duplicate Checking

Check-then-insert has a race. Use unique constraints at the database level.

5. Silent Overwrites Without Conflict Detection

Always check for conflicts before updating. Silent overwrites lose data.

Practice Questions

  1. What status code indicates a conflict error?
  2. When should you use 409 over 400?
  3. How does optimistic locking work for API updates?
  4. What information should a conflict response include?
  5. How do you prevent race conditions in duplicate detection?

Answers:

  1. 409 Conflict.
  2. When the request conflicts with server state, not when input is malformed.
  3. Clients send the version they read; the server rejects if it changed.
  4. Conflict field, values, existing resource URL, and resolution guidance.
  5. Use database unique constraints instead of check-then-insert.

Challenge: Implement optimistic locking for a document editing API. Include version tracking, conflict detection, and automatic resolution suggestions.

FAQ

What is the difference between 409 and 412?

: 409 is for resource state conflicts; 412 is for precondition failures (If-Match headers).

Should I use 409 for queue full errors?

: 503 Service Unavailable is more appropriate for capacity issues.

Can I use 409 for out-of-stock errors?

: Yes. The request conflicts with current inventory state.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro