hibernate/hibernate-orm · error · AnnotationException

Annotation '%s' is not assignable to '%s'

Error message

Annotation '%s' is not assignable to '%s'

What it means

castAnnotationType verifies that the attribute's type annotation is an instance of the first type argument of the AnnotationBasedUserType<A, ?> interface implemented by the custom type. A mismatched annotation triggers AnnotationException before the unsafe cast; the message names the custom type class and the interface type.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java:1200

		}

		@Override
		public Properties getParameters() {
			return parameters;
		}
	}

	private static <A extends Annotation> A castAnnotationType(
			Annotation typeAnnotation, AnnotationBasedUserType<A, ?> annotationBased ) {
		final var annotationType = annotationBased.getClass();
		for ( var iface: annotationType.getGenericInterfaces() ) {
			if ( iface instanceof ParameterizedType parameterizedType
					&& parameterizedType.getRawType() == AnnotationBasedUserType.class ) {
				final var typeArguments = parameterizedType.getActualTypeArguments();
				if ( typeArguments.length > 0
					&& typeArguments[0] instanceof Class<?> annotationClass ) {
					if ( !annotationClass.isInstance( typeAnnotation ) ) {
						throw new AnnotationException( String.format( "Annotation '%s' is not assignable to '%s'",
								annotationType.getName(), iface.getTypeName() ) );
					}
					@SuppressWarnings("unchecked") // safe, we just checked it
					final var castAnnotation = (A) typeAnnotation;
					return castAnnotation;
				}
			}
		}
		throw new AssertionFailure( "Could not find implementing interface" );
	}

	private <T extends UserType<?>, A extends Annotation> T instantiateUserType(
			Class<T> customType, A typeAnnotation,
			UserTypeCreationContext creationContext) {
		try {
			T userType;
			if ( typeAnnotation != null ) {
				@SuppressWarnings("unchecked") // totally safe

View on GitHub (pinned to fad1729dce)

Solutions

  1. Put the exact annotation matching the generic parameter on the attribute.
  2. Update the custom type's AnnotationBasedUserType<A, ...> parameter to the annotation actually used.
  3. Remove stray type annotations from the attribute so the right one is selected.

Example fix

// before
@OtherFormat
@CustomType(MyFormatType.class) // implements AnnotationBasedUserType<MyFormat, ?>
private String value;

// after
@MyFormat(style = "short")
@CustomType(MyFormatType.class)
private String value;
Defensive patterns

Strategy: validation

Validate before calling

static boolean annotationMatches( Class<? extends Annotation> found,
        Class<? extends AnnotationBasedUserType<?, ?>> userType ) {
    for ( var iface : userType.getGenericInterfaces() ) {
        if ( iface instanceof ParameterizedType pt
                && pt.getRawType() == AnnotationBasedUserType.class
                && pt.getActualTypeArguments()[0] instanceof Class<?> expected ) {
            return expected.isAssignableFrom( found );
        }
    }
    return false;
}

Prevention

When it happens

Trigger: Custom type implements AnnotationBasedUserType<MyAnn, ?> but the attribute carries a different custom annotation (@OtherAnn); multiple type annotations present where the wrong one is picked; refactoring the generic parameter without updating mappings.

Common situations: Renamed or repackaged annotation classes; two similar annotation-based types applied together; copy-paste of @CustomType without the matching annotation.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/d90c5a0fc59ea499. Report an issue: GitHub.