Using "org.springframework:spring-web:5.3.23"

TLDR

the bug is in MappingJackson2HttpMessageConverter. For ByteArray method canRead() returns true, but method read() fails. Here is my github repository where you can reproduce this bug https://github.com/SpeedyGonzaless/SpringWebBug/tree/master

Research

If you add MappingJackson2HttpMessageConverter to list of HttpMessageConverters with your hands (for example with extending WebMvcConfigurationSupport and overriding configureMessageConverters)

@Configuration
class ConverterConfiguration(
    private var builder: Jackson2ObjectMapperBuilder,
) : WebMvcConfigurationSupport() {

    override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>?>) {
        converters.add(converter())
        addDefaultHttpMessageConverters(converters)
    }

    @Bean
    fun converter(): MappingJackson2HttpMessageConverter? {
        val objectMapper = builder.build<ObjectMapper>()
        return MappingJackson2HttpMessageConverter(objectMapper)
    }

}

so the MappingJackson2HttpMessageConverter will be at the begging of list: Spring Bug with MappingJackson2HttpMessageConverter

Then when you will try to make http request with ByteArray in body

@PostMapping
fun countries(
    @RequestBody bytes: ByteArray
): String {
    return "Success"
}

Spring Bug with MappingJackson2HttpMessageConverter

Your application will fail with exception

2022-09-22 09:25:21.914  WARN 48389 --- [nio-8083-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `[B` from Object value (token `JsonToken.START_OBJECT`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `[B` from Object value (token `JsonToken.START_OBJECT`)<EOL> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 1]]

The reason is in class AbstractMessageConverterMethodArgumentResolver, method readWithMessageConverters in this code:

for (HttpMessageConverter<?> converter : this.messageConverters) {
  Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
  GenericHttpMessageConverter<?> genericConverter =
      (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
  if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
      (targetClass != null && converter.canRead(targetClass, contentType))) {
    if (message.hasBody()) {
      HttpInputMessage msgToUse =
          getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
      body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
          ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
      body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
    }
    else {
      body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
    }
    break;
  }
}

As MappingJackson2HttpMessageConverter is first in the list we get it, check with the method canRead(), it returns true and then we get exception when call wethod read().

But if we remove this code

override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>?>) {
    converters.add(converter())
    addDefaultHttpMessageConverters(converters)
}

then ByteArrayHttpMessageConverter would be first in list Spring Bug with MappingJackson2HttpMessageConverter and everything works fine Spring Bug with MappingJackson2HttpMessageConverter

Result:

the bug is in MappingJackson2HttpMessageConverter. For ByteArray method canRead() returns true, but method read() fails.

Comment From: poutsma

The MappingJackson2HttpMessageConverter is perfectly capable of reading (or writing) a byte array. For instance, this works fine:

byte[] bytes = "Foo Bar".getBytes(StandardCharsets.UTF_8);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
this.converter.write(bytes, MediaType.APPLICATION_JSON, new HttpOutputMessage() {
    @Override
    public OutputStream getBody() throws IOException {
        return bos;
    }

    @Override
    public HttpHeaders getHeaders() {
        return new HttpHeaders();
    }
});

System.out.println(bos.toString(StandardCharsets.UTF_8));

The above prints out: "Rm9vIEJhcg==", which is the BASE64 representation of "Foo Bar". Note that there is no wrapper JSON object, just BASE64.

If you want to handle a JSON response where the byte array is part of object, under the name bytes like you posted in the screenshot, then you have to read the request into a request object, something like:

private static class MyBytes {

    private byte[] bytes;

    public byte[] getBytes() {
        return this.bytes;
    }

    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }
}

In short: the JSON you POST does not match the @RequestBody annotated parameter.