quarkusio/quarkus · error · MessageBundleException

@MessageBundle must be declared on an interface: {bundleClas

Error message

@MessageBundle must be declared on an interface: {bundleClass}

What it means

Quarkus Qute message bundles are defined as Java interfaces annotated with @MessageBundle. During application build, MessageBundleProcessor.processBundles validates that every class carrying @MessageBundle is actually an interface; if it is a class, enum, or record, the build fails with this MessageBundleException. This is a compile/deploy-time contract check so template resolution can generate a implementing class.

Source

Thrown at extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java:253

                            } else {
                                files = localeToFiles.get(locale);
                                if (files == null) {
                                    files = new ArrayList<>();
                                    localeToFiles.put(locale, files);
                                }
                            }
                            files.add(messageFile);
                        }
                    }

                    // Check for duplicates again
                    checkForDuplicates(localeToMergeCandidates);
                    checkForDuplicates(localeToFiles);

                    bundles.add(new MessageBundleBuildItem(name, bundleClass, localeToInterface,
                            localeToFiles, localeToMergeCandidates, defaultLocale));
                } else {
                    throw new MessageBundleException("@MessageBundle must be declared on an interface: " + bundleClass);
                }
            }
        }

        // Detect interfaces annotated with @Localized that don't extend a message bundle interface
        for (AnnotationInstance localizedAnnotation : index.getAnnotations(Names.LOCALIZED)) {
            if (localizedAnnotation.target().kind() == Kind.CLASS) {
                ClassInfo localized = localizedAnnotation.target().asClass();
                if (Modifier.isInterface(localized.flags())) {
                    if (!localizedInterfaces.contains(localized.name())) {
                        throw new MessageBundleException(
                                "A localized message bundle interface must extend a message bundle interface: " + localized);
                    }
                } else {
                    throw new MessageBundleException("@Localized must be declared on an interface: " + localized);
                }
            }
        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the annotated type to a Java interface: `@MessageBundle(locale = "en") public interface AppMessages { ... }`
  2. If a class is required, move the bundle methods into a new interface and have the class consume the generated bundle bean instead.
  3. Rebuild and confirm the annotation target is reported as an interface in the error location.

Example fix

// before
@MessageBundle(locale = "en")
public class AppMessages {
    String hello(String name);
}

// after
@MessageBundle(locale = "en")
public interface AppMessages {
    String hello(String name);
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify every @MessageBundle target is an interface before building
Class<?> c = AppMessages.class;
if (!c.isInterface() || c.getAnnotation(MessageBundle.class) == null) {
    throw new IllegalStateException("@MessageBundle must be on an interface: " + c);
}

Type guard

static boolean isValidMessageBundle(Class<?> c) {
    return c.isInterface() && c.isAnnotationPresent(MessageBundle.class);
}

Prevention

When it happens

Trigger: Annotating a concrete class, abstract class, enum, or record with @MessageBundle instead of an interface, e.g. `@MessageBundle(locale="en") public class AppMessages {...}`.

Common situations: Copy-pasting the annotation from an interface onto a class; migrating a messages class from another i18n framework (e.g. ResourceBundle-based) without converting it to an interface; auto-generated code that emits classes.

Related errors


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