quarkusio/quarkus · error · IllegalArgumentException

Can not add converter ${converter.name()} that is not parame

Error message

Can not add converter ${converter.name()} that is not parameterized with a type

What it means

A SmallRye Config converter must implement Converter<T> parameterized with a concrete type so the framework knows which config value type it converts. If the converter class has no parameterized interface type (or is Object), getConverterType throws this IllegalArgumentException because the target type cannot be derived.

Source

Thrown at core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java:1232

            for (String prefix : entry.getValue()) {
                configClasses.add(ConfigClass.configClass(loadClass(entry.getKey()), prefix));
            }
        }
        return configClasses;
    }

    private static Class<?> loadClass(final String name) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            return classLoader.loadClass(name);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("The class (" + name + ") cannot be created during deployment.", e);
        }
    }

    private static Type getConverterType(final ClassInfo converter, final CombinedIndexBuildItem combinedIndex) {
        if (converter.name().toString().equals(Object.class.getName())) {
            throw new IllegalArgumentException(
                    "Can not add converter " + converter.name() + " that is not parameterized with a type");
        }

        for (Type type : converter.interfaceTypes()) {
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = type.asParameterizedType();
                if (parameterizedType.name().equals(CONVERTER_NAME)) {
                    List<Type> arguments = parameterizedType.arguments();
                    if (arguments.size() != 1) {
                        throw new IllegalArgumentException(
                                "Converter " + converter.name() + " must be parameterized with a single type");
                    }
                    return arguments.get(0);
                }
            }
        }

        return getConverterType(combinedIndex.getComputingIndex().getClassByName(converter.superName()), combinedIndex);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Parameterize the Converter interface with the concrete converted type, e.g. class ColorConverter implements Converter<Color>
  2. If a converter is intentionally generic, register it explicitly via @Priority/@RegisterConverter mechanisms that accept an explicit type rather than relying on inference
  3. Check that the class implements io.smallrye.config.Converter directly (via interfaceTypes) and not just an unrelated interface

Example fix

// before
public class ColorConverter implements Converter { ... }
// after
public class ColorConverter implements Converter<Color> { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

for (java.lang.reflect.Type iface : ColorConverter.class.getGenericInterfaces()) {
    if (iface instanceof ParameterizedType pt
            && pt.getRawType() == io.smallrye.config.Converter.class
            && pt.getActualTypeArguments().length == 1) {
        return; // OK
    }
}
throw new IllegalStateException("Converter must implement Converter<T> with one type argument");

Type guard

static boolean isValidConverter(Class<?> c) {
    return java.util.Arrays.stream(c.getGenericInterfaces())
        .anyMatch(t -> t instanceof ParameterizedType pt
            && pt.getRawType().equals(io.smallrye.config.Converter.class)
            && pt.getActualTypeArguments().length == 1
            && !(pt.getActualTypeArguments()[0] instanceof Object));
}

Prevention

When it happens

Trigger: A registered converter class implements raw Converter or Converter<Object> (i.e. no parameterized Converter interface with a real type argument) and is discovered during config generation.

Common situations: Writing a converter without generics (implements Converter instead of Converter<MyType>); after type-erasure refactors a raw type was introduced; anonymous/inner converter classes whose generic info was dropped.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/272ec14500eaf7eb. Report an issue: GitHub.