Código de status HTTP · Erros do cliente (4xx)
Erro 412 Precondition Failed
O erro 412 Precondition Failed significa que o pedido trazia uma condição, geralmente If-Match com um ETag, e o recurso no servidor não bate mais com ela. A causa mais comum é outra pessoa ter salvo o mesmo registro entre a sua leitura e a sua gravação.
| Classe | 4xx, Erros do cliente |
|---|---|
| Definido em | RFC 9110 §15.5.13 |
| Pode ir para o cache por padrão | Só com Cache-Control ou Expires explícitos |
| Pode repetir o pedido | Não às cegas; busque a versão atual, reaplique a alteração e repita com o novo ETag |
| Cabeçalhos relevantes |
|
O que significa o 412
A RFC 9110, seção 15.5.13, define o 412 como uma ou mais condições dos cabeçalhos do pedido darem falso no servidor. É o mecanismo por trás do controle de concorrência otimista: você lê o recurso, guarda o ETag e o devolve em If-Match junto com a alteração. Se alguém mudou o recurso no meio tempo, o ETag é outro e o servidor recusa com 412 em vez de sobrescrever o trabalho alheio sem avisar.
A seção 13.2.2 define a ordem de avaliação: primeiro If-Match, depois If-Unmodified-Since, depois If-None-Match. Um If-None-Match que falha em GET ou HEAD nem é erro; vira 304 Not Modified. Nos outros métodos vira 412, e é assim que If-None-Match: * transforma um PUT em "só cria se não existir". As gravações condicionais do Amazon S3 funcionam desse jeito e respondem 412 quando a chave do objeto já existe.
Causas comuns
Se você administra o servidor
- Duas pessoas, ou duas abas, editaram o mesmo registro; o segundo salvamento leva um ETag que já ficou velho.
- O cliente guardou o ETag de uma resposta antiga e o reutilizou depois que um processo em segundo plano atualizou o recurso.
- Um upload do tipo "só cria" (If-None-Match: *) mirou numa chave que já existe, como nas gravações condicionais do S3 ou do Google Cloud Storage.
- Um proxy ou camada de compressão reescreveu o ETag (o nginx transforma um ETag forte em fraco depois do gzip, por exemplo), então o If-Match nunca bate.
Como resolver
Se você administra o servidor
- No cliente, trate o 412: faça GET de novo, mescle ou reaplique a alteração do usuário e reenvie com o novo ETag. Mostre um aviso de conflito quando não der para mesclar automaticamente.
- Inclua o ETag atual na resposta 412 para o cliente saber o quanto está desatualizado.
- Se o If-Match nunca bate, compare o ETag enviado pelo cliente com o que a origem gera; a comparação forte falha com ETags que começam com W/.
Como enviar um 412
Os exemplos mostram só a resposta. No handler de verdade, compare o cabeçalho If-Match com o ETag armazenado e devolva 412 quando forem diferentes. O http.ServeContent do Go já avalia If-Match e If-Unmodified-Since em GET.
app.put('/documents/:id', (req, res) => {
res.set('ETag', '"v8"');
res.status(412).json({ error: 'Document changed since you loaded it' });
});// app/documents/[id]/route.ts
export async function PUT() {
return Response.json(
{ error: 'Document changed since you loaded it' },
{ status: 412, 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.StatusPreconditionFailed) // 412
w.Write([]byte(`{"error":"Document changed since you loaded it"}`))
})from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.put("/documents/{id}")
def update_document(id: str):
raise HTTPException(status_code=412, detail="Document changed since you loaded it", headers={"ETag": "\"v8\""})Costuma ser confundido com
- 412 vs 428
- O 428 indica que o cliente não mandou pré-condição e o servidor exige uma; o 412 indica que mandou e ela falhou.
- 412 vs 409
- O 409 é um conflito detectado pela lógica da aplicação; o 412 é um conflito detectado só pelos cabeçalhos condicionais do HTTP.
- 412 vs 304
- Um If-None-Match que falha em GET gera 304, sinal de cache válido; num método de escrita a mesma falha gera 412.
Perguntas frequentes
- Como o ETag evita que uma alteração sobrescreva outra?
- O cliente devolve em If-Match o ETag que leu. O servidor só grava se o ETag atual for idêntico; se não for, responde 412, e um cliente desatualizado não consegue sobrescrever uma versão mais nova.
- Devo usar 412 ou 409 para conflito de edição?
- Use 412 quando o conflito é detectado por If-Match ou If-Unmodified-Since. Use 409 quando a sua lógica encontra o conflito, por exemplo um número de versão no corpo JSON que não confere.
- Por que o S3 devolve 412 Precondition Failed?
- O pedido trazia uma condição, como If-None-Match: * no PutObject ou If-Match com um ETag, e o objeto não atendeu: a chave já existia ou tinha mudado.
Revisado em por Arielton Oberek.