Describe the bug Spring Cloud Config 4.3.0 Spring Boot 3.5.6

Scenario 1

application.yml

spring.application.name: demo1
spring.profiles.active: local
spring.config.import: "optional:configserver:"

Logs

2025-10-23T01:30:58.535-07:00  INFO 26151 --- [demo1] [           main] com.example.demo.Demo1ApplicationKt      : The following 1 profile is active: "local"
2025-10-23T01:30:58.548-07:00  INFO 26151 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:30:58.548-07:00  WARN 26151 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@2f7a7219 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'default']): None of labels [] found
2025-10-23T01:30:58.548-07:00  INFO 26151 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:30:58.548-07:00  WARN 26151 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@2c177f9e uris = array<String>['http://localhost:8888'], optional = true, profiles = 'local']): None of labels [] found
2025-10-23T01:30:58.548-07:00  INFO 26151 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:30:58.548-07:00  WARN 26151 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@5db4c359 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'default']): None of labels [] found

From logs, for some reason config is fetched three times (default, active-profile, default). First two are expected based on docs, but third time is not.

Scenario 2

application.yml

spring.application.name: demo1

spring.profiles.active: local

---
spring.config.activate.on-profile: local

spring.config.import: "optional:configserver:"

Logs

2025-10-23T01:39:24.398-07:00  INFO 30180 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:39:24.398-07:00  WARN 30180 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@3e587920 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'local']): None of labels [] found
2025-10-23T01:39:24.398-07:00  INFO 30180 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:39:24.398-07:00  WARN 30180 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@2ef8a8c3 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'default']): None of labels [] found

When the import is included in a profile-specific document, the fetches happen in reverse order (active-profile, default)

Scenario 3

application.yml

spring.application.name: demo1

spring.profiles.active: local

spring.config.import: "optional:configserver:http://localhost:8000"

---
spring.config.activate.on-profile: local

spring.config.import: "optional:configserver:"

Logs

2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://remote:8000
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Exception on Url - http://remote:8000:org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://remote:8000/demo1/default": remote. Will be trying the next url if available
2025-10-23T01:43:59.266-07:00  WARN 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@27cf3151 uris = array<String>['http://remote:8000'], optional = true, profiles = 'default']): I/O error on GET request for "http://remote:8000/demo1/default": remote
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:43:59.266-07:00  WARN 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@63fd4873 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'local']): None of labels [] found
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://localhost:8888
2025-10-23T01:43:59.266-07:00  WARN 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@1e11bc55 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'default']): None of labels [] found
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://remote:8000
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Exception on Url - http://remote:8000:org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://remote:8000/demo1/local": remote. Will be trying the next url if available
2025-10-23T01:43:59.266-07:00  WARN 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@7544a1e4 uris = array<String>['http://remote:8000'], optional = true, profiles = 'local']): I/O error on GET request for "http://remote:8000/demo1/local": remote
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Fetching config from server at : http://remote:8000
2025-10-23T01:43:59.266-07:00  INFO 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Exception on Url - http://remote:8000:org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://remote:8000/demo1/default": remote. Will be trying the next url if available
2025-10-23T01:43:59.266-07:00  WARN 32350 --- [demo1] [           main] o.s.c.c.c.ConfigServerConfigDataLoader   : Could not locate PropertySource ([ConfigServerConfigDataResource@70e0accd uris = array<String>['http://remote:8000'], optional = true, profiles = 'default']): I/O error on GET request for "http://remote:8000/demo1/default": remote

Looks like the order is 1. main-default 2. profile-active 3. profile-default 4. main-active 5. main-default

Sample Create new Spring Boot Project via intelliJ

Additional Question

The real use case in my project is that my config server url should be profile-dependent. In this case, what is the recommended approach for implementing this?

Something that looks like it could work

spring.application.name: demo1

spring.profiles.active: local
---
spring.config.activate.on-profile: local

spring.config.import: "optional:configserver:http://localhost:8000"
---
spring.config.activate.on-profile: dev

spring.config.import: "optional:configserver:http://localhost:8100"
---
spring.config.activate.on-profile: !local & !dev

# all other environments
spring.config.import: "optional:configserver:https://somedomain"

Comment From: ryanjbaxter

I think you might be seeing this behavior due to the client not being able to reach the config server. Can you try the same scenarios but with the config client being able to reach the config server?

Comment From: MCAxiaz

Confirmed that if the default profile config is fetched successfully it will not attempt to fetch a second time.

From what I can tell, config is loaded in Spring Boot's ConfigDataEnvironment$processAndApply() method

void processAndApply() {
        ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
                this.loaders);
        registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
        ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
        ConfigDataActivationContext activationContext = createActivationContext(
                contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
        contributors = processWithoutProfiles(contributors, importer, activationContext);
        activationContext = withProfiles(contributors, activationContext);
        contributors = processWithProfiles(contributors, importer, activationContext);
        applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
                importer.getOptionalLocations());
    }

In scenario 1:

In processInitial, config is loaded using default profile. Then in processWithProfiles, it attempts to load list of resolved config resources which is resolved by ConfigServerConfigDataLocationResolver to be both the default and active profile, but it will also apply check to make sure it does not reload already loaded config (so if default was loaded successfully in processInitial, it will be skipped)

Furthermore, it seems like the list of config is loaded in reverse order, so that's why active profile is loaded before default in processWithProfiles, resulting in the order of default -> active -> default (skipped if first load was successful)

In scenario 2:

processInitial does not load the default profile config. So processWithProfiles will load the default and active profiles (in reverse order as mentioned above), resulting in the order of active -> default


I'm not sure of the intended behaviour, but it seems to me it would make sense that processWithProfiles should not load the default profile?


Also, it seems if I specify the propertySources with the same name in both default and active profile, the config in default profile actually overrides the config in the active profile.

e.g.

response of default profile

{
      "name": "demo1",
      "profiles": [
        "default"
      ],
      "label": null,
      "version": null,
      "state": null,
      "propertySources": [
        {
          "name": "cloudConfig",
          "source": {
            "demo.feature.state": "default",
            "demo.feature.enabled": true
          }
        }
      ]
    }

response of active profile

{
      "name": "demo1",
      "profiles": [
        "local"
      ],
      "label": null,
      "version": null,
      "state": null,
      "propertySources": [
        {
          "name": "cloudConfig",
          "source": {
            "demo.feature.state": "local",
            "demo.feature.enabled": true
          }
        }
      ]
    }

demo.feature.state resolves to default

The property source uniqueness is based on name, which is same regardless of profile. new OriginTrackedMapPropertySource("configserver:" + source.getName(), map, true)

Because the property source of the active profile actually gets added to the list of propertySources first (which is expected as it should have higher priority), but that causes the property source of the default profile which gets added later to actually override it, since it shares the same name.

Perhaps the name should include the profile as well?

Comment From: MCAxiaz

Specifying spring.cloud.config.profile will prevent default profile from being loaded, since it will cause the data resource to always resolve to the specified profile.

Overall flow is not affected, as three fetches are still attempted to be made: specified-profile -> specified-profile -> specified-profile, but as before, if the first fetch succeeds, subsequent ones are skipped.

So explicitly setting spring.cloud.config.profile can prevent "extra" fetches.

Maybe documentation can be expanded to include this information?

Comment From: ryanjbaxter

If you would like to submit a PR with updated documentation I would be happy to take a look

Comment From: MCAxiaz

Sure thing.

And regarding the behaviour of default profile propertySource overriding that of the profile-specific propertySource, is that intended behaviour?