409 Conflict
The request could not be completed due to a conflict with the current state of the target resource.
Meaning & Description
Common Causes
- Duplicate entries (email, username, ID) in a creation request
- Race conditions when multiple users edit the same resource simultaneously
- Attempting an operation that violates business logic state rules
- Trying to delete a resource that has dependent child resources
How to fix a 409 error
- Check the response body for details on exactly which field or state caused the conflict.
- If it's a concurrency issue, fetch the latest version of the resource and apply your changes again.
- If it's a duplicate entry issue, use a different unique identifier (like a different email).
- Verify business logic rules to ensure the requested action is valid for the current state.
Browser & SEO Behaviour
Browser Behavior
Treated as a standard 4xx error. Browsers will just display the response payload.
SEO Impact
Neutral. Crawlers generally do not trigger 409s as they perform GET requests, which rarely cause state conflicts.
CDN Behavior
Not cached by default. Typically passes through to the origin.
Code Examples
app.post('/api/users', async (req, res) => {
const { email } = req.body;
const existingUser = await db.users.findOne({ email });
if (existingUser) {
return res.status(409).json({
error: 'Conflict',
message: 'A user with this email already exists.'
});
}
await db.users.create(req.body);
res.status(201).json({ message: 'User created' });
});from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.put("/documents/{doc_id}")
def update_document(doc_id: int, version: int, content: str):
current_doc = db.get_document(doc_id)
# Optimistic concurrency control
if current_doc.version != version:
raise HTTPException(
status_code=409,
detail="Document has been modified by another user. Fetch the latest version."
)
db.update_document(doc_id, content)
return {"status": "updated"}func updateHandler(w http.ResponseWriter, r *http.Request) {
err := updateDatabase(r.Context(), data)
if err == ErrDuplicateKey {
http.Error(w, "Record already exists", http.StatusConflict)
return
}
w.WriteHeader(http.StatusOK)
}[HttpPost("register")]
public async Task<IActionResult> Register(UserDto dto)
{
var exists = await _userService.EmailExistsAsync(dto.Email);
if (exists)
{
return Conflict(new { Message = "Email address is already taken." });
}
// ...
return Ok();
}curl -i -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"email": "existing@example.com"}'Raw HTTP Response Example
HTTP/1.1 409 Conflict
Content-Type: application/json
Content-Length: 68
{
"error": "Project name 'Project Alpha' is already in use."
}Real-world Examples
Frequently Asked Questions
What is the difference between 400 and 409?
400 means the request is syntactically invalid (e.g., missing a field). 409 means the request is valid, but applying it would break business rules or database constraints (e.g., a duplicate email).
When should I use 409 for concurrency?
Use 409 when you employ Optimistic Concurrency Control (e.g., passing a version number or ETag). If the client's version is older than the server's, return 409 to force them to fetch the newest data.
Is returning 409 for duplicate emails secure?
It is standard practice, but it exposes which emails are registered in your system. For highly sensitive applications, it's safer to return a generic success message and send an email explaining the conflict.
How is 409 different from 412 Precondition Failed?
412 is strictly used when HTTP conditional headers like `If-Match` evaluate to false. 409 is a more generic code for any application-level state conflict.
Should I return 409 when deleting something fails?
Yes, if the deletion is blocked because the resource has active children or dependencies (e.g., deleting a company that still has active employees), a 409 Conflict is the correct response.
Did You Know?
409 is the most common status code used to handle "Race Conditions" in distributed API systems.
WebDAV relies heavily on the 409 status code to manage file locks and concurrent edits.
Developer Tips
- Always provide enough context in the response body so the client programmatically knows *what* conflicted, allowing them to prompt the user or automatically retry.
- Use 409 over 400 when the issue can be resolved by the client updating their state (like picking a new username).
- For strict HTTP compliance in concurrency control, consider using 412 Precondition Failed in conjunction with `If-Match` ETags, reserving 409 for business logic conflicts.
Interview Questions
These are questions you might face in a backend or API design interview that touch on HTTP 409.
What status code would you return if a user tries to register with an email that is already in the database?
409 Conflict is the standard response, as the request violates a uniqueness constraint in the current state of the system.
How do you handle two users updating the same record at exactly the same time in a REST API?
I would implement Optimistic Concurrency Control using version numbers or ETags. If User B submits an update based on stale data, the server rejects it with a 409 Conflict, forcing them to fetch User A's changes first.
Explain the difference between 409 Conflict and 422 Unprocessable Entity.
422 means the data was semantically invalid (e.g., a password is too short). 409 means the data is perfectly valid, but it conflicts with the server's current state (e.g., the username is taken).
Common Interview Mistakes
- Using 400 Bad Request for duplicate entries instead of 409 Conflict.
- Failing to mention Optimistic Concurrency Control when discussing 409.
- Confusing 409 with 500 Internal Server Error when a database constraint exception is thrown.