Código de status HTTP · Erros do cliente (4xx)
Erro 415 Unsupported Media Type
O erro 415 Unsupported Media Type significa que o servidor não processa o corpo do pedido no formato enviado. Na grande maioria das vezes o corpo é JSON, mas o cabeçalho Content-Type está ausente ou diz text/plain ou formulário.
| Classe | 4xx, Erros do cliente |
|---|---|
| Definido em | RFC 9110 §15.5.16 |
| Pode ir para o cache por padrão | Só com Cache-Control ou Expires explícitos |
| Pode repetir o pedido | Sim, depois de corrigir o Content-Type ou o formato do corpo |
| Cabeçalhos relevantes |
|
O que significa o 415
A RFC 9110, seção 15.5.16, diz que o problema pode vir do Content-Type declarado, do Content-Encoding ou da própria inspeção dos dados pelo servidor. Ela também explica como ajudar o cliente: listar na resposta 415 as codificações aceitas em Accept-Encoding, ou os tipos de mídia aceitos em Accept.
Os frameworks se comportam de jeitos bem diferentes. Controllers do Spring MVC e do ASP.NET Core com [FromBody] respondem 415 sozinhos quando o Content-Type não tem leitor registrado. O Express não: o express.json() só ignora pedidos que não são application/json, e o req.body fica undefined. O FastAPI costuma responder 422, porque a validação falha por falta do corpo.
Um detalhe do navegador causa muitos desses erros: fetch() com corpo em string e sem cabeçalhos envia Content-Type: text/plain;charset=UTF-8. Na aba de rede parece JSON, mas o servidor recebe texto puro.
Causas comuns
Se você administra o servidor
- fetch() chamado com body: JSON.stringify(dados) mas sem cabeçalho Content-Type, e o navegador rotula como text/plain.
- O curl -d envia application/x-www-form-urlencoded por padrão; uma API JSON recusa, a menos que você adicione -H "Content-Type: application/json".
- Um upload enviado como multipart/form-data para um endpoint que espera binário ou JSON, ou o contrário.
- O corpo do pedido com Content-Encoding gzip ou br que o servidor não descompacta; o body-parser com inflate: false responde 415 nesse caso.
- Um charset que o endpoint recusa, como application/json; charset=latin1 numa API rígida.
Como resolver
Se você administra o servidor
- Defina o cabeçalho que corresponde ao corpo: headers: { "Content-Type": "application/json" } no fetch, ou deixe o axios definir passando um objeto em vez de string.
- No curl, use --json '{"a":1}' (curl 7.82+), que define Content-Type e Accept como JSON.
- Em uploads de arquivo, passe um objeto FormData e não defina Content-Type na mão; o navegador inclui o boundary do multipart.
- No servidor, devolva um cabeçalho Accept junto com o 415 e uma mensagem com o tipo esperado, para quem integra não precisar adivinhar.
Como enviar um 415
app.post('/orders', express.json(), (req, res) => {
if (!req.is('application/json')) {
res.set('Accept', 'application/json');
return res.status(415).json({ error: 'Send the body as application/json' });
}
// ...
});// app/orders/route.ts
export async function POST(request: Request) {
const type = request.headers.get('content-type') ?? '';
if (!type.startsWith('application/json')) {
return Response.json(
{ error: 'Send the body as application/json' },
{ status: 415, headers: { Accept: 'application/json' } }
);
}
const order = await request.json();
// ...
}mux.HandleFunc("POST /orders", func(w http.ResponseWriter, r *http.Request) {
mediatype, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil || mediatype != "application/json" {
w.Header().Set("Accept", "application/json")
http.Error(w, "send the body as application/json", http.StatusUnsupportedMediaType) // 415
return
}
// ...
})from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
@app.post("/orders")
async def create_order(request: Request):
if not request.headers.get("content-type", "").startswith("application/json"):
raise HTTPException(status_code=415, detail="Send the body as application/json",
headers={"Accept": "application/json"})
order = await request.json()
...# Form-encoded by default: a JSON API may answer 415
curl -X POST -d '{"sku":"A1"}' https://api.example.com/orders
# Correct: declare the type (or use --json on curl 7.82+)
curl -X POST -H 'Content-Type: application/json' -d '{"sku":"A1"}' https://api.example.com/ordersCostuma ser confundido com
- 415 vs 422
- O 422 indica que o formato foi entendido mas o conteúdo é inválido (falta um campo obrigatório); o 415 indica que o próprio formato não é aceito.
- 415 vs 406
- O 406 trata do formato de resposta pedido no Accept; o 415 trata do formato do pedido que o cliente enviou.
- 415 vs 400
- O 400 serve para um corpo com o Content-Type certo que não pode ser lido, como um JSON quebrado.
Perguntas frequentes
- Por que recebo 415 ao mandar JSON com fetch?
- Porque o fetch() com corpo em string usa Content-Type: text/plain;charset=UTF-8 por padrão. Inclua headers: { "Content-Type": "application/json" } na requisição.
- Como resolver o erro 415 no Spring Boot?
- O parâmetro @RequestBody não tem conversor para o Content-Type recebido. Envie Content-Type: application/json, ou, se o cliente manda um formulário, use @ModelAttribute ou @RequestParam no lugar de @RequestBody.
- Devo definir Content-Type em upload multipart?
- Não manualmente. Quando você passa um FormData para o fetch ou o axios, o navegador define multipart/form-data com o boundary certo. Definir na mão tira o boundary e o servidor não consegue separar as partes.
- Content-Type errado é 415 ou 400?
- É 415. A RFC 9110 o define exatamente para conteúdo num formato que o recurso não aceita, seja pelo Content-Type, pelo Content-Encoding ou pela inspeção dos dados.
Revisado em por Arielton Oberek.