Código de status HTTP · Erros do cliente (4xx)
Erro 409 Conflict
O erro 409 Conflict significa que o pedido não pôde ser aplicado porque conflita com o estado atual do recurso, e o cliente talvez consiga resolver o conflito e tentar de novo. As causas mais comuns são criar algo que já existe (e-mail ou nome duplicado) e salvar uma edição baseada numa versão desatualizada.
| Classe | 4xx, Erros do cliente |
|---|---|
| Definido em | RFC 9110 §15.5.10 |
| Pode ir para o cache por padrão | Só com Cache-Control ou Expires explícitos |
| Pode repetir o pedido | Depois de resolver o conflito, por exemplo buscando a versão mais recente e reaplicando a mudança |
| Cabeçalhos relevantes |
|
O que significa o 409
A RFC 9110, seção 15.5.10, diz que o 409 serve para conflitos com o estado atual do recurso que o usuário talvez consiga resolver, e que a resposta deve explicar o conflito o bastante para ele reconhecer a origem. O exemplo da RFC é um PUT com mudanças que conflitam com uma edição anterior feita por outra pessoa.
Sistemas reais usam assim. O Kubernetes responde 409 quando você atualiza um objeto com resourceVersion antigo ("the object has been modified; please apply your changes to the latest version") e quando tenta criar um que já existe. O Elasticsearch responde 409 em conflito de versão, e o Amazon S3 responde 409 BucketAlreadyExists quando o nome do bucket já está em uso.
Use 409 para conflito de estado, não para entrada inválida. Um e-mail malformado é 400 ou 422; um e-mail válido mas já cadastrado é 409, porque o mesmo pedido daria certo com outro estado do servidor.
Causas comuns
Se você está visitando o site
- Você tentou se cadastrar com um e-mail ou nome de usuário que já existe.
- Outra pessoa editou o mesmo registro enquanto você estava com ele aberto, e o seu salvamento partiu da versão antiga.
- Você clicou duas vezes no botão de enviar e o segundo pedido tentou criar a mesma coisa de novo.
Se você administra o servidor
- Uma restrição de unicidade no banco (e-mail, slug, ID externo) recusou o insert, e a API traduz isso em 409.
- Concorrência otimista: o cliente enviou um número de versão ou ETag que não bate mais com o armazenado.
- Uma transição de estado não permitida no momento, como cancelar um pedido que já foi enviado.
- Operações no estilo Git, como enviar ou mesclar num branch que já avançou.
Como resolver
Se você está visitando o site
- Recarregue a página para pegar a versão mais recente, refaça a alteração e salve de novo.
- No cadastro, use outro e-mail ou tente entrar ou redefinir a senha da conta que já existe.
Se você administra o servidor
- Devolva um corpo que diga o que conflitou (qual campo, qual versão atual) para o cliente resolver sem adivinhar.
- No cliente, trate o 409 relendo o recurso, mesclando ou perguntando ao usuário, e tente de novo com a versão nova.
- Use chaves de idempotência em endpoints de criação para um clique duplo devolver o primeiro resultado em vez de conflito.
- Capture as violações de unicidade de forma explícita; deixá-las subir transforma um 409 limpo em 500.
Como enviar um 409
app.put('/documents/:id', (req, res) => {
res.set('ETag', '"v8"');
res.status(409).json({ error: 'Document was changed by someone else', current_version: 8 });
});// app/documents/[id]/route.ts
export async function PUT() {
return Response.json(
{ error: 'Document was changed by someone else', current_version: 8 },
{ status: 409, headers: { 'ETag': '"v8"' } }
);
}mux.HandleFunc("PUT /documents/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("ETag", `"v8"`)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusConflict) // 409
w.Write([]byte(`{"error":"Document was changed by someone else","current_version":8}`))
})from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.put("/documents/{id}")
def update_document(id: str):
return JSONResponse(
status_code=409,
content={"error": "Document was changed by someone else", "current_version": 8},
headers={"ETag": '"v8"'},
)Costuma ser confundido com
- 409 vs 412
- O 412 é a versão com pré-condição: o cliente mandou If-Match e o ETag não bateu. O 409 cobre conflitos que o servidor detecta sem cabeçalhos condicionais.
- 409 vs 422
- O 422 indica que a entrada em si é inválida; o 409 indica entrada válida que conflita com o que já existe.
- 409 vs 400
- O 400 é para pedidos malformados em qualquer situação; um pedido com 409 daria certo com outro estado do servidor.
Perguntas frequentes
- E-mail duplicado deve retornar 409 ou 400?
- O 409 se encaixa melhor: o pedido é válido, mas conflita com um registro existente. Algumas equipes preferem 422 ou 400 para não revelar quais e-mails estão cadastrados, uma troca razoável por privacidade em formulários de cadastro.
- Qual a diferença entre 409 e 412?
- O 412 Precondition Failed é disparado por um cabeçalho condicional, como If-Match, que não bateu. O 409 é o servidor detectando sozinho um conflito com o estado atual, por exemplo por um campo de versão no corpo.
- O cliente pode repetir um 409 automaticamente?
- Não às cegas: o mesmo pedido vai conflitar de novo. Repita depois de reler o recurso e reaplicar a mudança, o que alguns clientes fazem sozinhos em contadores ou mesclas simples.
- Por que o Kubernetes devolve 409 Conflict?
- Ou o objeto já existe na criação, ou você atualizou com um resourceVersion que não é mais o atual porque outra pessoa mudou o objeto. Busque o objeto mais recente, reaplique a mudança e atualize de novo.
Revisado em por Arielton Oberek.