quarkusio/quarkus · error · PanacheQueryException

The annotation ProjectedFieldName must have a non-empty valu

Error message

The annotation ProjectedFieldName must have a non-empty value.

What it means

getNameFromProjectedFieldName reads the value() member of a @ProjectedFieldName-style annotation found on a constructor parameter. If the annotation is present but its value is the empty string, Panache throws PanacheQueryException because an empty name cannot be mapped to any select item. This is a direct misuse of the annotation's contract that value must be a non-empty field name.

Source

Thrown at extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/ProjectionConstructorUtil.java:208

        return false;
    }

    private static boolean hasAnnotation(AnnotatedElement annotatedElement, String annotationTypeName) {
        for (java.lang.annotation.Annotation annotation : annotatedElement.getAnnotations()) {
            if (annotationTypeName.equals(annotation.annotationType().getName())) {
                return true;
            }
        }
        return false;
    }

    private static String getNameFromProjectedFieldName(AnnotatedElement annotatedElement) {
        for (java.lang.annotation.Annotation annotation : annotatedElement.getAnnotations()) {
            if (PROJECTED_FIELD_NAME_ANNOTATIONS.contains(annotation.annotationType().getName())) {
                try {
                    String name = (String) annotation.annotationType().getMethod("value").invoke(annotation);
                    if (name.isEmpty()) {
                        throw new PanacheQueryException("The annotation ProjectedFieldName must have a non-empty value.");
                    }
                    return name;
                } catch (ReflectiveOperationException e) {
                    throw new PanacheQueryException("Unable to read ProjectedFieldName value", e);
                }
            }
        }
        throw new PanacheQueryException("Missing ProjectedFieldName annotation");
    }

    private static String buildNoSuitableConstructorMessage(Class<?> type, Constructor<?> rejected) {
        StringBuilder message = new StringBuilder("No suitable projection constructor found for ")
                .append(type.getName())
                .append(" (rejected constructor: ")
                .append(rejected)
                .append(").");
        if (isKotlinClass(type)) {
            message.append(" Kotlin value classes and default parameters may produce synthetic constructors.")

View on GitHub (pinned to e1c734241f)

Solutions

  1. Set the annotation value to the actual select item/attribute name: @ProjectedFieldName("fullName")
  2. If the parameter name already matches, remove the annotation entirely so the compiler-provided name is used
  3. Add validation on dynamically generated DTOs to assert annotation values are non-empty

Example fix

// before
public PersonDto(@ProjectedFieldName("") String name) { ... }

// after
public PersonDto(@ProjectedFieldName("name") String name) { ... }
Defensive patterns

Strategy: validation

Validate before calling

static void assertProjectedFieldNameNonEmpty(java.lang.reflect.Parameter p) {
    ProjectedFieldName a = p.getAnnotation(ProjectedFieldName.class);
    if (a != null && a.value().isEmpty()) {
        throw new IllegalStateException("@ProjectedFieldName on " + p + " must be non-empty");
    }
}

Try / catch

try {
    return query.project(Dto.class).list();
} catch (PanacheQueryException e) {
    if (e.getMessage().contains("non-empty value")) {
        throw new IllegalStateException("Fix empty @ProjectedFieldName values on " + Dto.class, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Declaring @ProjectedFieldName("") (or an aliased annotation whose value resolves to "") on a projection constructor parameter, then running a query that triggers projection construction.

Common situations: Placeholder left from code generation; programmatically built annotation values from config that resolved to empty; copy-paste where the value was deleted accidentally.

Related errors


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