Given the following:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

import java.util.EnumMap;
import java.util.List;

@EnableConfigurationProperties(DemoApplication.ExampleProperties.class)
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    public enum Example {
        ONE, TWO, THREE;
    }

    @ConfigurationProperties(prefix = "example")
    record ExampleProperties(List<ExampleEntry> entries) {}

    record ExampleEntry(String name, EnumMap<Example, String> map) {}
}

and configuration:

example:
  entries:
    - name: one
      map:
        ONE: one
        TWO: two
        THREE: three

Will result in the following exception:

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

Description:

Failed to bind properties under 'example.entries[0].map' to java.util.EnumMap<com.example.demo.DemoApplication$Example, java.lang.String>:

    Property: example.entries[0].name
    Value: "one"
    Origin: class path resource [application.yml] - 3:13
    Reason: java.lang.IllegalArgumentException: Cannot create EnumMap for unknown key type

Action:

Update your application's configuration

It works if I switch to Map instead of EnumMap:

- public record ExampleEntry(String name, EnumMap<Example, String> map) {}
+ public record ExampleEntry(String name, Map<Example, String> map) {}

But the keys (for my use case) are enums and it makes sense to use the specialized map implementation.

After some debugging, it looks to be an issue on Boot's side in ValueObjectBinder:

https://github.com/spring-projects/spring-boot/blob/3648f5f494e6b5bb010bb7f881eb748cbc0f8cc5/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java#L160

The key portion is lost which then fails Framework's check:

https://github.com/spring-projects/spring-framework/blob/5715b2a783b383c7cae214c2e37efb43f48d924f/spring-core/src/main/java/org/springframework/core/CollectionFactory.java#L319

Additionally, maybe related: https://github.com/spring-projects/spring-boot/issues/19156

Comment From: ciscoo

Simplified example ExampleProperties to a record.

Comment From: wilkinsona

Thanks for the report. There are two similar problems here. One's in ValueObjectBinder to which you've linked, but it will only occur if the EnumMap record component or constructor parameter is annotated with @DefaultValue. The other is in MapBinder.bindAggregate(ConfigurationPropertyName, Bindable<?>, AggregateElementBinder). I think it's the latter that's causing the failure in your case. Regardless, both need to be updated to handle an EnumMap by providing the key type to CollectionFactory.