quarkusio/quarkus · error · NotSupportedException

The content-type header value did not match the value in @Co

Error message

The content-type header value did not match the value in @Consumes

What it means

RESTEasy Reactive throws this NotSupportedException (HTTP 415) when the request's Content-Type header cannot be matched against the @Consumes media types declared on the target resource method. The library parses the header via MediaTypeHelper.valueOf and checks compatibility with the method's consumed types using getFirstMatch; if no match exists, it rejects the request per the JAX-RS spec. This happens in validateConsumes during request routing, before the resource method is invoked.

Source

Thrown at independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/HandlerMediaTypeUtil.java:36

import org.jboss.resteasy.reactive.server.mapping.RuntimeResource;

class HandlerMediaTypeUtil {

    private static final String INVALID_ACCEPT_HEADER_MESSAGE = "The accept header value did not match the value in @Produces";
    private static final String MALFORMED_ACCEPT_HEADER_MESSAGE = "The accept header value did not correspond to a valid media type";

    // according to the spec we need to return HTTP 415 when content-type header doesn't match what is specified in @Consumes
    // HttpMethod being null means this is a sub resource locator method. The handler chain of the sub resource has to match the content-type header
    static void validateConsumes(RequestMapper.RequestMatch<RuntimeResource> target,
            ResteasyReactiveRequestContext requestContext) {
        if (target.value.getHttpMethod() != null && !target.value.getConsumes().isEmpty()) {
            String contentType = (String) requestContext.getHeader(HttpHeaders.CONTENT_TYPE, true);
            if (contentType != null) {
                try {
                    if (MediaTypeHelper.getFirstMatch(
                            target.value.getConsumes(),
                            Collections.singletonList(MediaTypeHelper.valueOf(contentType))) == null) {
                        throw new NotSupportedException("The content-type header value did not match the value in @Consumes");
                    }
                } catch (IllegalArgumentException e) {
                    throw new NotSupportedException("The content-type header value did not correspond to a valid media type");
                }
            }
        }
    }

    // according to the spec we need to return HTTP 406 when Accept header doesn't match what is specified in @Produces.
    // A fully unparseable Accept header is a client syntax error and returns HTTP 400 instead.
    // HttpMethod being null means this is a sub resource locator method. The handler chain of the sub resource has to match the accept header
    static void validateProduces(RequestMapper.RequestMatch<RuntimeResource> target,
            ResteasyReactiveRequestContext requestContext) {
        if (target.value.getHttpMethod() != null && target.value.getProduces() != null) {
            // there could potentially be multiple Accept headers and we need to response with 406
            // if none match the method's @Produces
            List<String> accepts = (List<String>) requestContext.getHeader(HttpHeaders.ACCEPT, false);
            if (!accepts.isEmpty()) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Set the client request's Content-Type header to one of the media types in the endpoint's @Consumes annotation.
  2. Widen or add media types on the server, e.g. change @Consumes(MediaType.APPLICATION_JSON) to @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}).
  3. Remove the restrictive @Consumes annotation entirely to accept any content type (wildcard).

Example fix

// before
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String create(Form form) { ... }
// after
@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED})
public String create(Form form) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// client-side, before sending
String contentType = "application/json";
if (!contentType.matches("[\w!#$&^_.+-]+/[\w!#$&^_.+-]+.*")) {
    throw new IllegalArgumentException("Invalid Content-Type: " + contentType);
}
// ensure it matches the endpoint's declared @Consumes

Try / catch

try {
    Response r = client.post(entity);
} catch (NotSupportedException | ProcessingException e) {
    // check response status 415 and log the Content-Type sent
}

Prevention

When it happens

Trigger: A request is routed to a resource method with a non-empty @Consumes declaration, the Content-Type header is present, MediaTypeHelper.getFirstMatch(target.value.getConsumes(), [parsed content-type]) returns null — e.g. sending Content-Type: text/plain to a method annotated @Consumes(MediaType.APPLICATION_JSON).

Common situations: Clients posting form data (application/x-www-form-urlencoded) to JSON-only endpoints; frontends sending application/json but the endpoint declared text/plain or vice versa; copying endpoint code and forgetting to update @Consumes; API clients defaulting to text/xml or missing charset variants.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/56c57a6683c58ee0. Report an issue: GitHub.