quarkusio/quarkus · error · IllegalArgumentException

${method.name()} of Repository ${repositoryClassInfo} is mea

Error message

${method.name()} of Repository ${repositoryClassInfo} is meant to be a delete query and can therefore only have a void or long return type

What it means

A @Modifying query starting with 'delete' maps to JpaOperations.delete and may only return void, long, or java.lang.Long (the affected row count). Any other declared return type is rejected at build time with this IllegalArgumentException because there is no way to materialize other result shapes from a delete statement.

Source

Thrown at extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/CustomQueryMethodsAdder.java:228

                for (int i = 0; i < methodParameterTypes.size(); i++) {
                    params[i] = mc.parameter("p" + i);
                }

                mc.body(bc -> {
                    // Store static field and instance field in LocalVars so they can be reused
                    LocalVar ops = bc.localVar("ops", bc.getStaticField(operationsField));
                    LocalVar entityClass = bc.localVar("entityClass",
                            bc.get(mc.this_().field(entityClassFieldDescriptor)));

                    if (isModifying) {
                        AnnotationInstance modifyingAnnotation = method.annotation(DotNames.SPRING_DATA_MODIFYING);
                        handleFlushAutomatically(modifyingAnnotation, bc, entityClass);

                        if (finalQueryString.toLowerCase().startsWith("delete")) {
                            if (!DotNames.PRIMITIVE_LONG.equals(methodReturnTypeDotName)
                                    && !DotNames.LONG.equals(methodReturnTypeDotName)
                                    && !DotNames.VOID.equals(methodReturnTypeDotName)) {
                                throw new IllegalArgumentException(
                                        method.name() + " of Repository " + repositoryClassInfo
                                                + " is meant to be a delete query and can therefore only have a void or long return type");
                            }

                            // we need to strip 'delete' or else JpaOperations.delete will generate the wrong query
                            String deleteQueryString = finalQueryString.substring("delete".length());
                            Expr deleteCount;
                            if (!finalNamedParameterToIndex.isEmpty()) {
                                Expr parameters = generateParametersObject(finalNamedParameterToIndex, bc, params);

                                // call JpaOperations.delete
                                deleteCount = bc.invokeVirtual(
                                        MethodDesc.of(AbstractManagedJpaOperations.class, "delete", long.class,
                                                Class.class, String.class, Parameters.class),
                                        ops, entityClass,
                                        Const.of(deleteQueryString), parameters);
                            } else {
                                Expr paramsArray = generateParamsArray(queryParameterIndexes, bc, params);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the return type to long, Long, or void
  2. Replace int/Integer with long/Long (Quarkus does not accept int for delete counts here)
  3. If no count is needed, declare void and ignore the return value
  4. Split the method: do a paginated/select query separately, then a void delete

Example fix

// before
@Modifying
@Query("delete from Token t where t.expiry < :now")
int deleteExpired(Instant now);
// after
@Modifying
@Query("delete from Token t where t.expiry < :now")
long deleteExpired(Instant now);
Defensive patterns

Strategy: validation

Validate before calling

if (query.trim().toLowerCase().startsWith("delete")) {
    Class<?> rt = method.getReturnType();
    if (!(rt.equals(void.class) || rt.equals(long.class) || rt.equals(Long.class)))
        throw new IllegalArgumentException("Delete @Query must return void or long/Long");
}

Try / catch

try {
    compile();
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("delete query and can therefore only have")) {
        log.error("Change return type to long/Long/void");
    }
    throw e;
}

Prevention

When it happens

Trigger: A repository method with @Query("delete ...") (with or without @Modifying) declared to return int, Integer, boolean, List<T>, an entity type, or any type other than long/Long/void.

Common situations: Porting from JPA's executeUpdate() which returns int and declaring int in the repository; copy-pasting a select method signature; Spring allows int/Integer there while Quarkus's Spring Data layer is more restrictive and requires long/Long/void.

Related errors


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