quarkusio/quarkus · error · java.lang.IllegalArgumentException

Message bundle interface must be annotated either with @Mess

Error message

Message bundle interface must be annotated either with @MessageBundle or with @Localized: 

What it means

Message bundle interfaces must be annotated with @MessageBundle (base bundle) or @Localized (localized variants). MessageBundles.get() checks for either annotation before doing the CDI lookup and throws IllegalArgumentException when neither is present.

Source

Thrown at extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageBundles.java:45

public final class MessageBundles {

    public static final String ATTRIBUTE_LOCALE = TemplateInstance.LOCALE;
    public static final String DEFAULT_LOCALE = "<<default>>";

    private MessageBundles() {
    }

    public static <T> T get(Class<T> bundleInterface) {
        return get(bundleInterface, null);
    }

    public static <T> T get(Class<T> bundleInterface, Localized localized) {
        if (!bundleInterface.isInterface()) {
            throw new IllegalArgumentException("Not a message bundle interface: " + bundleInterface.getName());
        }
        if (!bundleInterface.isAnnotationPresent(MessageBundle.class)
                && !bundleInterface.isAnnotationPresent(Localized.class)) {
            throw new IllegalArgumentException(
                    "Message bundle interface must be annotated either with @MessageBundle or with @Localized: "
                            + bundleInterface.getName());
        }
        ArcContainer container = Arc.requireContainer();
        InstanceHandle<T> handle = localized != null ? container.instance(bundleInterface, localized)
                : container.instance(bundleInterface);
        if (handle.isAvailable()) {
            return handle.get();
        }
        throw new IllegalStateException(Qute.fmt(
                "Unable to obtain a message bundle for interface [{ifacename}]{#if loc} and locale [{loc.value}]{/if}")
                .data("ifacename", bundleInterface.getName())
                .data("loc", localized)
                .render());
    }

    /**
     * Obtains a message bundle for the specified interface and the current locale.

View on GitHub (pinned to e1c734241f)

Solutions

  1. Annotate the interface with @MessageBundle (e.g. @MessageBundle(locale="en"))
  2. For localized variants use @Localized instead
  3. Verify the annotation is from io.quarkus.qute.i18n, not another similarly named annotation

Example fix

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

Strategy: validation

Validate before calling

boolean annotated = MyMessages.class.isAnnotationPresent(MessageBundle.class)
    || MyMessages.class.isAnnotationPresent(Localized.class);
if (!annotated) throw new IllegalStateException("Add @MessageBundle or @Localized to " + MyMessages.class);

Type guard

static <T> boolean hasBundleAnnotation(Class<T> iface) {
    return iface.isAnnotationPresent(MessageBundle.class) || iface.isAnnotationPresent(Localized.class);
}

Try / catch

try {
    MyMessages m = MessageBundles.get(MyMessages.class);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("must be annotated either with @MessageBundle")) {
        throw new IllegalStateException("Annotate the interface with @MessageBundle", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling MessageBundles.get(iface) where iface is an interface but lacks both @MessageBundle and @Localized annotations — e.g. an unannotated plain interface passed by mistake.

Common situations: Defining a bundle interface and forgetting @MessageBundle; passing an unrelated interface to MessageBundles.get; generating interfaces without applying the annotation.

Related errors


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