apache/incubator-seata · error · IllegalArgumentException

custom config type name %s is not allowed

Error message

custom config type name %s is not allowed

What it means

Thrown by CustomConfigurationProvider.provide() when the value of custom.name equals the name of a built-in ConfigType (e.g. nacos, apollo, zk, consul, etcd3, file, custom). Seata rejects it because the Custom type exists precisely to delegate to non-built-in providers; a built-in name here would either recurse (custom) or shadow native handling.

Source

Thrown at config/seata-config-custom/src/main/java/org/apache/seata/config/custom/CustomConfigurationProvider.java:42

import org.apache.seata.config.ConfigurationFactory;
import org.apache.seata.config.ConfigurationKeys;
import org.apache.seata.config.ConfigurationProvider;

import java.util.stream.Stream;

@LoadLevel(name = "Custom")
public class CustomConfigurationProvider implements ConfigurationProvider {
    @Override
    public Configuration provide() {
        String pathDataId = ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
                + ConfigType.Custom.name().toLowerCase() + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
                + "name";
        String name = ConfigurationFactory.CURRENT_FILE_INSTANCE.getConfig(pathDataId);
        if (StringUtils.isBlank(name)) {
            throw new IllegalArgumentException("name value of custom config type must not be blank");
        }
        if (Stream.of(ConfigType.values()).anyMatch(ct -> ct.name().equalsIgnoreCase(name))) {
            throw new IllegalArgumentException(String.format("custom config type name %s is not allowed", name));
        }
        return EnhancedServiceLoader.load(ConfigurationProvider.class, name).provide();
    }
}

View on GitHub (pinned to e01f97c6db)

Solutions

  1. If you want a built-in backend, drop the Custom indirection: set `config.type = nacos` (etc.) directly
  2. If you truly have a custom provider, give it a name that does not collide with any ConfigType enum constant, e.g. 'mycompany-config'
  3. If delegating to a built-in via your own wrapper, register the wrapper under a distinct @LoadLevel name

Example fix

# before
config:
  type: custom
  custom:
    name: nacos

# after
config:
  type: nacos
  name: seata-server.properties
Defensive patterns

Strategy: validation

Validate before calling

if (Stream.of(ConfigType.values()).anyMatch(ct -> ct.name().equalsIgnoreCase(name))) throw new IllegalStateException("custom.name must not be a built-in type name");

Try / catch

try { provider.provide(); } catch (IllegalArgumentException e) { log.error(e.getMessage()); /* fall back to direct built-in type */ }

Prevention

When it happens

Trigger: `config.type = custom` combined with `custom.name = nacos` (or file, zk, apollo, consul, etcd3, redis, custom — matched case-insensitively). The stream check Stream.of(ConfigType.values()).anyMatch(ct -> ct.name().equalsIgnoreCase(name)) fires before the SPI load.

Common situations: Copy-pasting an existing config block and changing only `type: custom` while leaving the old backend name in place, or misunderstanding Custom as an alias indirection for built-in types.

Related errors


AI-assisted analysis of apache/incubator-seata@e01f97c6db (2026-08-14). Data as JSON: /api/errors/20fa8542afccfb8f. Report an issue: GitHub.