quarkusio/quarkus · error · RuntimeException
Unknown recorder constructor parameter: %s in recorder %s
Error message
Unknown recorder constructor parameter: %s in recorder %s
What it means
Recorder constructor parameters that are generic (ParameterizedType) must be RuntimeValue<T>; any other parameterized type is unsupported. Quarkus unwraps RuntimeValue's type argument and otherwise cannot map the generic type to a runtime-injectable value, so it throws during loadStepsFromClass.
Source
Thrown at core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java:575
}
methodParamFns.add((bc, bri) -> {
assert bri != null;
return bri.getRecordingProxy(parameterClass);
});
//now look for recorder parameter injection
//as we now inject config directly into recorders we need to look at the constructor params
Constructor<?>[] ctors = parameter.getType().getDeclaredConstructors();
for (var ctor : ctors) {
if (ctors.length == 1 || ctor.isAnnotationPresent(Inject.class)) {
for (var type : ctor.getGenericParameterTypes()) {
Class<?> theType;
boolean isRuntimeValue = false;
if (type instanceof ParameterizedType pt) {
if (pt.getRawType().equals(RuntimeValue.class)) {
theType = (Class<?>) pt.getActualTypeArguments()[0];
isRuntimeValue = true;
} else {
throw new RuntimeException("Unknown recorder constructor parameter: " + type
+ " in recorder " + parameter.getType());
}
} else {
theType = (Class<?>) type;
}
ConfigRoot annotation = theType.getAnnotation(ConfigRoot.class);
if (annotation != null) {
methodConsumingConfigPhases.add(recordAnnotation.value() == ExecutionTime.STATIC_INIT
? ConfigPhase.BUILD_AND_RUN_TIME_FIXED
: annotation.phase());
if (annotation.phase().isAvailableAtBuild() && !annotation.phase().isAvailableAtRun()) {
throw reportError(parameter, annotation.phase() + " configuration "
+ type.getTypeName()
+ " cannot be consumed in a Recorder");
} else if (annotation.phase().isReadAtMain() && !isRuntimeValue) {
throw reportError(parameter, annotation.phase() + " configuration "
+ type.getTypeName()
+ " can only be injected in a @Recorder constructor as a RuntimeValue<"View on GitHub (pinned to e1c734241f)
Solutions
- Change the parameter type to RuntimeValue<T> if a generic wrapped value is intended.
- Use a non-generic class type for the constructor parameter (e.g. a custom config class annotated @ConfigRoot, or a plain class).
- Pass the generic data at record time via a recorder method call instead of the constructor.
Example fix
// before
public MyRecorder(List<String> names) { ... }
// after
public MyRecorder(RuntimeValue<List<String>> names) { ... }
// or: use a concrete type
public MyRecorder(String[] names) { ... } Defensive patterns
Strategy: validation
Validate before calling
for (Type t : ctor.getGenericParameterTypes()) {
if (t instanceof ParameterizedType pt && !pt.getRawType().equals(RuntimeValue.class))
throw new IllegalArgumentException("Unsupported generic recorder param: " + t);
} Type guard
static boolean isAllowedRecorderParam(Type t) {
if (t instanceof ParameterizedType pt) {
return pt.getRawType().equals(RuntimeValue.class);
}
return t instanceof Class<?>;
} Prevention
- Only use RuntimeValue<T> for generic recorder constructor parameters.
- Pass generic data via recorder method calls, not constructor generics.
- Keep recorder constructors limited to concrete classes or @ConfigRoot types.
When it happens
Trigger: Declaring a recorder constructor parameter like List<String>, Optional<Foo>, Map<K,V> or any other parameterized type that is not RuntimeValue<T>; the raw type check pt.getRawType().equals(RuntimeValue.class) fails.
Common situations: Injecting collections or config-annotated generics into a Recorder constructor expecting Quarkus to resolve them; after upgrading Quarkus which tightened the allowed parameter types.
Related errors
- %s is marked @Record but does not inject an @Recorder object
- Failed to record call to method ${call.method}
- All parameters have already been loaded, it is too late to c
- String too large to record: ${param}
- Invalid proxy passed to recorder. ${rp} was created in a run
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/57de58ef133c7ff9.
Report an issue: GitHub.