Affects: 6.0.8


issue description: When setting a org.springframework.http.MediaType via config properties (e.g. application/json) it works well when running in jvm, for example by ./gradlew bootRun. But when running natively, for example by ./gradlew nativeRun it fails on startup with a property binding issue: failed to convert java.lang.String to org.springframework.http.MediaType (caused by java.lang.IllegalArgumentException: Invalid token character '/' in token "application/json") (full error below). It is possible to fix the native version by specifying the media type as application-json but this let's the jvm version fail on startup with a similar issue.

This makes it hard to develop and debug on jvm and deploy as native image, when you use some general default config for both.

reproduce: I encountered this when working with Spring Data REST, when I was setting spring.data.rest.default-media-type, but it also can be reproduced by setting up your own config properties which accept a MediaType.

full error when starting native build with application/json (bootRun works fine)

***************************
APPLICATION FAILED TO START
***************************

Description: Failed to bind properties under 'spring.data.rest.default-media-type' to org.springframework.http.MediaType:
Property: spring.data.rest.default-media-type
Value: "application/json"
Origin: class path resource [application.yml] - 13:27
Reason: failed to convert java.lang.String to org.springframework.http.MediaType (caused by java.lang.IllegalArgumentException: Invalid token character '/' in token "application/json")
Action: Update your application's configuration 

full error when starting jvm build with application-json (nativeRun works fine)

***************************
APPLICATION FAILED TO START
***************************

Description: Failed to bind properties under 'spring.data.rest.default-media-type' to org.springframework.http.MediaType:
Property: spring.data.rest.default-media-type
Value: "application-json"
Origin: class path resource [application.yml] - 13:27
Reason: failed to convert java.lang.String to org.springframework.http.MediaType (caused by org.springframework.util.InvalidMimeTypeException: Invalid mime type "application-json": does not contain '/')
Action: Update your application's configuration

possible workarounds: - in general, having multiple configs for developing (jvm) and deployment/production (native) - for this specific case with Spring Data REST, setting the media type programmatically via config.setDefaultMediaType()


If you like I can prepare a repository with a minimal test case.

Thanks for your work

Comment From: bclozel

In your case, it looks like the value given to MediaType for parsing is "application-json". This is of course an invalid media type and the exception thrown by Spring Framework is expected in this case. I'm not sure how this value is being parsed in the configuration though. Which Spring Boot version are you using? Can you reproduce that in a sample application and share it with us?

I've reproduced a slightly different problem with the latest Spring Boot 3.0.x by adding the GraalVM and spring-data-rest starter, and binding to this property:

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'spring.data.rest.default-media-type' to org.springframework.http.MediaType:

    Property: spring.data.rest.default-media-type
    Value: "application/json"
    Origin: class path resource [application.yml] - 4:27
    Reason: failed to convert java.lang.String to org.springframework.http.MediaType (caused by java.lang.IllegalArgumentException: Invalid token character '/' in token "application/json")

Action:

Update your application's configuration

Here, the value is not incorrect but still cannot be converted to a MediaType instance. This looks like a configuration properties binding issue in native mode.

The detailed exception shows that binding to MediaType from application properties here fails because the binding process is using the MimeType default constructor which doesn't expect a full media type.

org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'spring.data.rest.default-media-type' to org.springframework.http.MediaType
    at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:387) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:347) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$4(Binder.java:472) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:98) ~[na:na]
    at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:86) ~[na:na]
    at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:62) ~[na:na]
    at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:476) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:590) ~[na:na]
    at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:576) ~[na:na]
    at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:474) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:414) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:332) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:262) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:249) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bind(ConfigurationPropertiesBinder.java:93) ~[mediatype:na]
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:96) ~[mediatype:na]
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:79) ~[mediatype:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:419) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1762) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:327) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:267) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:202) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory$1.orderedStream(DefaultListableBeanFactory.java:471) ~[na:na]
    at org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration.lambda$new$16(RepositoryRestMvcConfiguration.java:270) ~[mediatype:4.0.5]
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:245) ~[mediatype:3.0.5]
    at org.springframework.data.util.Lazy.get(Lazy.java:114) ~[mediatype:3.0.5]
    at org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration.repositoryRestConfiguration(RepositoryRestMvcConfiguration.java:382) ~[mediatype:4.0.5]
    at org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration__BeanDefinitions.lambda$getRepositoryRestConfigurationInstanceSupplier$7(RepositoryRestMvcConfiguration__BeanDefinitions.java:191) ~[na:na]
    at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:63) ~[mediatype:6.0.8]
    at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:51) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$withGenerator$0(BeanInstanceSupplier.java:171) ~[na:na]
    at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) ~[mediatype:6.0.8]
    at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:204) ~[na:na]
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[mediatype:6.0.8]
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:216) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:204) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:327) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:267) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:202) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:327) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:267) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:202) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:327) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:267) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:202) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:651) ~[mediatype:6.0.8]
    at org.springframework.boot.convert.ApplicationConversionService.addBeans(ApplicationConversionService.java:276) ~[na:na]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.addFormatters(WebMvcAutoConfiguration.java:319) ~[mediatype:na]
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite.addFormatters(WebMvcConfigurerComposite.java:81) ~[na:na]
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration.addFormatters(DelegatingWebMvcConfiguration.java:78) ~[mediatype:6.0.8]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration.mvcConversionService(WebMvcAutoConfiguration.java:511) ~[mediatype:na]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration__BeanDefinitions$EnableWebMvcConfiguration__BeanDefinitions.lambda$getMvcConversionServiceInstanceSupplier$6(WebMvcAutoConfiguration__BeanDefinitions.java:182) ~[na:na]
    at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:63) ~[mediatype:6.0.8]
    at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:51) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$withGenerator$0(BeanInstanceSupplier.java:171) ~[na:na]
    at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) ~[mediatype:6.0.8]
    at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:204) ~[na:na]
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[mediatype:6.0.8]
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:216) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:204) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:327) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:267) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:202) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[mediatype:6.0.8]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) ~[mediatype:6.0.8]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:917) ~[mediatype:6.0.8]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[mediatype:6.0.8]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[mediatype:3.0.6]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[mediatype:3.0.6]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[mediatype:3.0.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[mediatype:3.0.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[mediatype:3.0.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[mediatype:3.0.6]
    at com.example.mediatype.MediatypeApplication.main(MediatypeApplication.java:10) ~[mediatype:na]
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.springframework.http.MediaType] for value [application/json]
    at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:117) ~[na:na]
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[na:na]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192) ~[mediatype:6.0.8]
    at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:109) ~[na:na]
    at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:100) ~[na:na]
    at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:92) ~[na:na]
    at org.springframework.boot.context.properties.bind.Binder.bindProperty(Binder.java:459) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:403) ~[mediatype:3.0.6]
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343) ~[mediatype:3.0.6]
    ... 161 common frames omitted
Caused by: java.lang.IllegalArgumentException: Invalid token character '/' in token "application/json"
    at org.springframework.util.MimeType.checkToken(MimeType.java:224) ~[mediatype:6.0.8]
    at org.springframework.util.MimeType.<init>(MimeType.java:183) ~[mediatype:6.0.8]
    at org.springframework.util.MimeType.<init>(MimeType.java:134) ~[mediatype:6.0.8]
    at org.springframework.util.MimeType.<init>(MimeType.java:123) ~[mediatype:6.0.8]
    at org.springframework.http.MediaType.<init>(MediaType.java:478) ~[mediatype:6.0.8]
    at java.base@17.0.5/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[mediatype:na]
    at java.base@17.0.5/java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[mediatype:na]
    at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:113) ~[na:na]
    ... 169 common frames omitted

I'm moving this issue to Spring Boot for now - if it turns out this is not a binder issue, but a more general conversion issue in Spring Framework we will move it back here.

Comment From: mockxe

hi and thanks for moving the issue, I wasn't sure where to put it.

regarding your comment: this is exactly the issue I was referring to. Intuitively I would configure a media type with a slash / (e.g. application/json), this is also what my IDE suggests. This works well when doing a classic jvm build&run. I use ./gradlew bootRun.

I also added GraalVM ("GraalVM Native Support", as it's called in Spring Initializr), which gives me the additional gradle task nativeRun. When I use this (or any other build that incorporates GraalVM native image build) to run the application, I get the same binding issue that you got.

Apparently, when using a native build, the constructor expects a media type with a dash - (e.g. application-json), the second option I proposed in my initial report. This works well in native builds, but is not compatible with jvm builds.

Ideally jvm and native build would use the same constructor to accept the same format.

I put together a minimal test case for you: https://github.com/mockxe/native-property-issue It was created using Spring Initializr with framework version 6.0.8 and boot version 3.0.9. I'm on a Liberica Native Image Kit

openjdk version "17.0.6" 2023-01-17 LTS
OpenJDK Runtime Environment GraalVM 22.3.1 (build 17.0.6+10-LTS)
OpenJDK 64-Bit Server VM GraalVM 22.3.1 (build 17.0.6+10-LTS, mixed mode, sharing)

Please see the test cases README.md for further information

I hope this information helps you.

Comment From: wilkinsona

On the JVM, the creation of the MediaType is performed by MediaTypeEditor:

"main" spring-projects/spring-boot#1 prio=5 os_prio=31 cpu=4988.82ms elapsed=183.75s tid=0x00007fd37a00b000 nid=0x2203 at breakpoint [0x0000700000992000]
   java.lang.Thread.State: RUNNABLE
    at org.springframework.http.MediaType.<init>(MediaType.java:557)
    at org.springframework.http.MediaType.parseMediaType(MediaType.java:745)
    at org.springframework.http.MediaTypeEditor.setAsText(MediaTypeEditor.java:37)
    at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:425)
    at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:398)
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:155)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
    at org.springframework.boot.context.properties.bind.BindConverter$TypeConverterConverter.convert(BindConverter.java:221)
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
    at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:109)
    at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:100)
    at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:92)
    at org.springframework.boot.context.properties.bind.Binder.bindProperty(Binder.java:459)
    at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:403)
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343)
    …

Comment From: firasrg

Hello! I'd like to contribute here, it's my first time 😇. Is it possible ?

Comment From: quaff

ObjectToObjectConverter is used if spring-boot-starter-data-rest present

     Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.springframework.http.MediaType] for value [application/json]
       org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:117)
       org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
       org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
       org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:109)
       org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:100)
       [...]
     Caused by: java.lang.IllegalArgumentException: Invalid token character '/' in token "application/json"
       org.springframework.util.MimeType.checkToken(MimeType.java:224)
       org.springframework.util.MimeType.<init>(MimeType.java:183)
       org.springframework.util.MimeType.<init>(MimeType.java:134)
       org.springframework.util.MimeType.<init>(MimeType.java:123)
       org.springframework.http.MediaType.<init>(MediaType.java:478)
       [...]

ConverterNotFoundException is thrown if we change spring-boot-starter-data-rest to spring-boot-starter-web

     Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.springframework.http.MediaType]
       org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:118)
       org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:100)
       org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:92)
       org.springframework.boot.context.properties.bind.Binder.bindProperty(Binder.java:459)
       org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:403)
       [...]

Comment From: wilkinsona

The problem's occurring because MediaTypeEditor is hidden due to a lack of reflection hint metadata for it. This prevents BeanUtils.findEditorByConvention(Class<?>) from creating and returning a MediaTypeEditor instance. A workaround is to provide a RuntimeHintsRegistar that registers the required reflection hint:

static class PropertyEditorRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        hints.reflection().registerType(MediaTypeEditor.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
    }

}

This registrar must be imported using @ImportRuntimeHints(PropertyEditorRuntimeHints.class).

Comment From: quaff

The problem's occurring because MediaTypeEditor is hidden due to a lack of reflection hint metadata for it. This prevents BeanUtils.findEditorByConvention(Class<?>) from creating and returning a MediaTypeEditor instance. A workaround is to provide a RuntimeHintsRegistar that registers the required reflection hint:

```java static class PropertyEditorRuntimeHints implements RuntimeHintsRegistrar {

@Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { hints.reflection().registerType(MediaTypeEditor.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); }

} ```

This registrar must be imported using @ImportRuntimeHints(PropertyEditorRuntimeHints.class).

It doesn't explain the difference I posted above.

Comment From: wilkinsona

@quaff I'm not sure how that difference relates to this issue. If you don't use Spring Data REST then the spring.data.rest.default-media-type property will no longer be bound. Without some more information, it's not clear to me how the two different exceptions can occur. Perhaps you can share some more information that demonstrates the connection?

Comment From: quaff

spring.data.rest.default-media-type

@wilkinsona I'm using @mockxe's project, but spring.data.rest.default-media-type deleted and only foo.media-type=application/json is used, if you change the dependency spring-boot-starter-data-rest to spring-boot-starter-web, the exception changed also.

Comment From: wilkinsona

Thanks, @quaff. With Spring Data REST on the classpath, the following reflection hints are generated:

  {
    "name": "org.springframework.http.MediaType",
    "allDeclaredConstructors": true,
    "allDeclaredFields": true,
    "methods": [
      {
        "name": "getQualityValue",
        "parameterTypes": [ ]
      }
    ]
  }

When spring-boot-starter-web is used instead, these hints are not generated. Without reflective access to the constructors, ObjectToObjectConverter won't try (and fail) to reflectively create a MediaType instance. I don't have time at the moment to track down the exact source of these hints but I suspect they're coming from Spring Data REST or Spring HATEOAS.

Comment From: sdeleuze

I was a bit nervous adding hints for all PropertyEditor given the fact there are a lot of them and they are not all frequently needed, so for now let's introduce them on per use case basis when reported by users.