error http

HTTP 409 Conflict

Understanding HTTP 409 Conflict - the request conflicts with the current state of the server, such as duplicate entries or version conflicts.

What It Means

HTTP 409 Conflict indicates that the request could not be completed due to a conflict with the current state of the target resource. This is typically used when the client attempts to modify a resource in a way that would create an inconsistent state.

The server should include information in the response body to help the user resolve the conflict.

Common Causes

  • Attempting to create a resource that already exists (duplicate username, email)
  • Concurrent edits to the same resource (optimistic locking conflict)
  • Conflicting state transitions (e.g., trying to publish an already-published article)
  • Version mismatch when using ETags for optimistic concurrency control
  • Git-like merge conflicts in collaborative editing

How to Fix

Server-side: Handle duplicate resources

// Express.js - Handle unique constraint violations
app.post('/api/users', async (req, res) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json(user);
  } catch (error) {
    if (error.code === 11000 || error.code === '23505') {
      // MongoDB duplicate key or PostgreSQL unique violation
      return res.status(409).json({
        error: 'Conflict',
        message: 'A user with this email already exists',
        field: 'email'
      });
    }
    throw error;
  }
});

Optimistic concurrency control

// Server: Check version before updating
app.put('/api/articles/:id', async (req, res) => {
  const article = await Article.findById(req.params.id);
  const clientVersion = req.headers['if-match'];

  if (clientVersion && clientVersion !== article.version) {
    return res.status(409).json({
      error: 'Conflict',
      message: 'The resource has been modified by another user',
      currentVersion: article.version,
      yourVersion: clientVersion
    });
  }

  article.set(req.body);
  article.version = generateNewVersion();
  await article.save();

  res.set('ETag', article.version).json(article);
});

Client-side: Handle conflicts

async function updateArticle(id, data, version) {
  const response = await fetch(`/api/articles/${id}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'If-Match': version
    },
    body: JSON.stringify(data)
  });

  if (response.status === 409) {
    const conflict = await response.json();
    // Prompt user to resolve conflict or auto-merge
    const latestData = await fetch(`/api/articles/${id}`).then(r => r.json());
    return resolveConflict(data, latestData);
  }

  return response.json();
}

Python SQLAlchemy

from sqlalchemy.exc import IntegrityError

try:
    db.session.add(new_user)
    db.session.commit()
except IntegrityError:
    db.session.rollback()
    return jsonify({
        "error": "Conflict",
        "message": "A user with this email already exists"
    }), 409
  • HTTP 400 - Bad Request: The request itself is malformed, not a state conflict.
  • HTTP 422 - Unprocessable Entity: The request is syntactically valid but semantically invalid.