quarkusio/quarkus · error · NullPointerException

Value not set for ${param}

Error message

Value not set for ${param}

What it means

When recording an annotation proxy (an @AnnotationProxyBuilder-generated literal for a recorder method taking an annotation type), Quarkus reads each annotation member via its accessor. If a member has no value set (including from its default) it throws this NullPointerException, because the generated annotation literal would have an undefined member.

Source

Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java:962

                    constructorParamsHandles[iterator.previousIndex()] = loadObjectInstance(explicitValue, existing,
                            explicitValue.getClass(), relaxedValidation);
                } else {
                    AnnotationValue value = annotationValues.get(valueMethod.name());
                    if (value == null) {
                        // method.invokeInterfaceMethod(MAP_PUT, valuesHandle, method.load(entry.getKey()), loadObjectInstance(method, entry.getValue(),
                        // returnValueResults, entry.getValue().getClass()));
                        Object defaultValue = annotationProxy.getDefaultValues().get(valueMethod.name());
                        if (defaultValue != null) {
                            constructorParamsHandles[iterator.previousIndex()] = loadObjectInstance(defaultValue, existing,
                                    defaultValue.getClass(), relaxedValidation);
                            continue;
                        }
                        if (value == null) {
                            value = valueMethod.defaultValue();
                        }
                    }
                    if (value == null) {
                        throw new NullPointerException("Value not set for " + param);
                    }
                    DeferredParameter retValue = loadValue(value, annotationProxy.getAnnotationClass(), valueMethod);
                    constructorParamsHandles[iterator.previousIndex()] = retValue;
                }
            }
            return new DeferredArrayStoreParameter(annotationProxy.getAnnotationLiteralType()) {
                @Override
                ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
                    MethodDescriptor constructor = MethodDescriptor.ofConstructor(annotationProxy.getAnnotationLiteralType(),
                            constructorParams.stream().map(m -> m.returnType().name().toString()).toArray());
                    ResultHandle[] args = new ResultHandle[constructorParamsHandles.length];
                    for (int i = 0; i < constructorParamsHandles.length; i++) {
                        DeferredParameter deferredParameter = constructorParamsHandles[i];
                        if (deferredParameter instanceof DeferredArrayStoreParameter) {
                            DeferredArrayStoreParameter arrayParam = (DeferredArrayStoreParameter) deferredParameter;
                            arrayParam.doPrepare(context);
                        }
                        args[i] = context.loadDeferred(deferredParameter);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Call the setter for the missing member on the AnnotationProxyBuilder before passing it to the recorder — the message names the unset member
  2. Provide a default value in the annotation definition so unset members are tolerated
  3. Check the annotation class for newly added members after an upgrade and set them
  4. Verify you are invoking the correct value method name matching the annotation member

Example fix

// before
AnnotationProxyBuilder<MyAnno> b = new AnnotationProxyBuilder<>(MyAnno.class);
// 'value' never set, no default in @MyAnno
recorder.register(b.built());
// after
AnnotationProxyBuilder<MyAnno> b = new AnnotationProxyBuilder<>(MyAnno.class);
b.withValue("default");
recorder.register(b.built());
Defensive patterns

Strategy: validation

Validate before calling

// before passing an annotation proxy to a recorder
for (Method m : annotationType.getDeclaredMethods()) {
    Object v = builder.getValue(m.getName());
    if (v == null && m.getDefaultValue() == null) {
        throw new IllegalStateException("Annotation member not set and has no default: " + m.getName());
    }
}

Try / catch

try {
    recorder.register(proxyBuilder.built());
} catch (NullPointerException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Value not set for")) {
        throw new IllegalStateException("Set all annotation members without defaults: " + e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Creating an annotation proxy with AnnotationProxyBuilder for an annotation whose member method was never configured, and the annotation's member has no defaultValue() — then passing that proxy into a recorder method.

Common situations: Extension build steps synthesizing annotation instances to register classes (e.g. @Named, custom qualifiers) but forgetting to set a required member; annotation definitions changed upstream to add a member without a default; typos in member setter names.

Related errors


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