quarkusio/quarkus · error · PanacheQueryException

Missing ProjectedFieldName annotation

Error message

Missing ProjectedFieldName annotation

What it means

getNameFromProjectedFieldName iterates a parameter's annotations looking for one whose type name is in PROJECTED_FIELD_NAME_ANNOTATIONS; if none is found it throws PanacheQueryException("Missing ProjectedFieldName annotation"). This indicates Panache needed an explicit field-name mapping (typically because parameter names are not compiled in) but the required annotation was absent on that parameter.

Source

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

        }
        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.")
                    .append(" Use @ProjectedConstructor, @ProjectedFieldName, or a DTO with plain property types such as Long.");
        } else {
            message.append(" Use @ProjectedConstructor or @ProjectedFieldName to select a usable constructor,")
                    .append(" and ensure the application is built with parameter names (-parameters).");
        }
        return message.toString();
    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Annotate every constructor parameter with @ProjectedFieldName("entityFieldName")
  2. Rebuild the application with -parameters so annotations are not strictly required
  3. Ensure annotations are placed on constructor parameters, not class fields
  4. Rebuild all modules of the DTO after adding annotations (stale classes keep the error)

Example fix

// before
public PersonDto(String name, @ProjectedFieldName("age") int years) { ... }

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

Strategy: validation

Validate before calling

static void assertAllParametersAnnotated(java.lang.reflect.Constructor<?> c) {
    for (java.lang.reflect.Parameter p : c.getParameters()) {
        if (!p.isNamePresent() && !p.isAnnotationPresent(ProjectedFieldName.class)) {
            throw new IllegalStateException("Parameter " + p + " needs @ProjectedFieldName or compile with -parameters");
        }
    }
}

Try / catch

try {
    return query.project(Dto.class).list();
} catch (PanacheQueryException e) {
    if (e.getMessage().contains("Missing ProjectedFieldName")) {
        throw new IllegalStateException("Annotate all parameters of " + Dto.class + " constructor", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A constructor parameter without @ProjectedFieldName reaching the name-resolution path where reflection cannot supply a name — i.e. class compiled without -parameters and no other name source, so the fallback demands the annotation and fails when missing.

Common situations: Annotating only some constructor parameters and missing one; applying the annotation to fields instead of constructor parameters; classpath mismatch where an old compiled DTO lacks both -parameters and annotations.

Related errors


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