quarkusio/quarkus · error · IllegalArgumentException

The '@Version' annotation cannot be used on primitive types.

Error message

The '@Version' annotation cannot be used on primitive types. Offending entity is '${entity}'.

What it means

When generating the save() method, Quarkus distinguishes new vs existing entities by checking the @Version field. Spring Data JPA semantics require the version to be a nullable wrapper type (e.g. Long, Integer); a primitive version can never be null so newness cannot be detected. Quarkus rejects primitive @Version fields at augmentation time.

Source

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

                        if (isPersistable(entityDotName)) {
                            Expr isNew = bc.invokeVirtual(
                                    ClassMethodDesc.of(ClassDesc.of(entityDotName.toString()), "isNew",
                                            MethodTypeDesc.of(ConstantDescs.CD_boolean)),
                                    entityParam);
                            bc.ifElse(isNew,
                                    tb -> generatePersistAndReturn(entityParam, tb, opsVar),
                                    fb -> generateMergeAndReturn(entityParam, fb, opsVar,
                                            entityClassVar));
                        } else {
                            AnnotationTarget idAnnotationTarget = getIdAnnotationTarget(entityDotName, index);
                            Expr idValue = generateObtainValue(bc, entityDotName, entityParam, idAnnotationTarget);
                            Type idType = getTypeOfTarget(idAnnotationTarget);
                            Optional<AnnotationTarget> versionValueTarget = getVersionAnnotationTarget(entityDotName, index);

                            if (versionValueTarget.isPresent()) {
                                Type versionType = getTypeOfTarget(versionValueTarget.get());
                                if (versionType instanceof PrimitiveType) {
                                    throw new IllegalArgumentException(
                                            "The '@Version' annotation cannot be used on primitive types. Offending entity is '"
                                                    + entityDotName + "'.");
                                }
                                Expr versionValue = generateObtainValue(bc, entityDotName, entityParam,
                                        versionValueTarget.get());
                                bc.ifElse(bc.isNull(versionValue),
                                        tb -> generatePersistAndReturn(entityParam, tb, opsVar),
                                        fb -> generateMergeAndReturn(entityParam, fb, opsVar,
                                                entityClassVar));
                                // if version is present, we've handled both branches, so return here
                                return;
                            }

                            if (idType instanceof PrimitiveType) {
                                if (!idType.name().equals(DotNames.PRIMITIVE_LONG)
                                        && !idType.name().equals(DotNames.PRIMITIVE_INTEGER)) {
                                    throw new IllegalArgumentException(
                                            "Id type of '" + entityDotName + "' is invalid.");

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the @Version field type from primitive to its wrapper: int -> Integer, long -> Long
  2. Use java.util.Optional-friendly nullable types and adjust getters/setters accordingly
  3. If the schema column is NOT NULL, add a sensible default or let the persistence provider initialize the wrapper on insert

Example fix

// before
@Version
private long version;
// after
@Version
private Long version;
Defensive patterns

Strategy: validation

Validate before calling

// Verify @Version field is a wrapper type at entity design time
Field v = MyEntity.class.getDeclaredField("version");
if (v.getType().isPrimitive())
    throw new IllegalStateException("@Version must not be primitive: " + v.getType());

Type guard

boolean isValidVersionField(Field f) {
    return f.isAnnotationPresent(jakarta.persistence.Version.class)
        && !f.getType().isPrimitive();
}

Prevention

When it happens

Trigger: An entity has a field annotated @Version with a primitive type (int, long, short, etc.); calling save() on that repository triggers generation of the version-comparison logic.

Common situations: Defining optimistic-locking version columns with int/long instead of Integer/Long; porting entities from codebases where the provider tolerated primitives.

Related errors


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