quarkusio/quarkus · error
Template extension method declared on must be static:
Error message
Template extension method declared on must be static:
What it means
Qute template extension methods (value resolvers exposed via @TemplateExtension or namespace extension methods) must be static. ExtensionMethodGenerator.validate() throws IllegalStateException during build-time bytecode generation when the annotated method is an instance method, since Qute calls the method statically without a receiver instance.
Source
Thrown at independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java:81
public static final DotName TEMPLATE_EXTENSION = DotName.createSimple(TemplateExtension.class.getName());
public static final DotName TEMPLATE_ATTRIBUTE = DotName.createSimple(TemplateExtension.TemplateAttribute.class.getName());
public static final String SUFFIX = "_Extension" + ValueResolverGenerator.SUFFIX;
public static final String NAMESPACE_SUFFIX = "_Namespace" + SUFFIX;
public static final String MATCH_NAME = "matchName";
public static final String MATCH_NAMES = "matchNames";
public static final String MATCH_REGEX = "matchRegex";
public static final String PRIORITY = "priority";
public static final String NAMESPACE = "namespace";
public static final String PATTERN = "pattern";
public ExtensionMethodGenerator(IndexView index, ClassOutput classOutput) {
super(index, classOutput);
}
public static void validate(MethodInfo method, String namespace) {
if (!Modifier.isStatic(method.flags())) {
throw new IllegalStateException(
"Template extension method declared on " + method.declaringClass().name() + " must be static: " + method);
}
if (method.returnType().kind() == Kind.VOID) {
throw new IllegalStateException("Template extension method declared on " + method.declaringClass().name()
+ " must not return void: " + method);
}
if (Modifier.isPrivate(method.flags())) {
throw new IllegalStateException("Template extension method declared on " + method.declaringClass().name()
+ " must not be private: " + method);
}
}
/**
*
* @param method
* @param matchName
* @param matchNames
* @param matchRegexView on GitHub (pinned to e1c734241f)
Solutions
- Add the static modifier to the extension method.
- If the method needs instance state, make it static and pass required values as parameters, or use a different resolver approach (e.g. a ValueResolver/template data).
- Ensure the class containing the method is indexed (not needed for static fix, but the class must be part of the app).
- Rebuild with ./mvnw install after the change; the error occurs during augmentation.
Example fix
// before
@TemplateExtension
String shout(String s) { return s.toUpperCase() + "!"; }
// after
@TemplateExtension
static String shout(String s) { return s.toUpperCase() + "!"; } Defensive patterns
Strategy: validation
Validate before calling
// check before build
for (MethodInfo m : extensionMethods) {
if (!Modifier.isStatic(m.flags())) {
throw new IllegalStateException("@TemplateExtension method must be static: " + m);
}
} Try / catch
try {
quarkusBuild augmentation...
} catch (IllegalStateException e) {
if (e.getMessage().contains("must be static")) {
// add 'static' to the annotated extension method
}
} Prevention
- Always declare @TemplateExtension methods static
- Do not attach @TemplateExtension to CDI bean instance methods
- If state is needed, pass values as parameters or use @TemplateData/ValueResolver instead
When it happens
Trigger: Declaring a template extension method as non-static — e.g. @TemplateExtension on an instance method of a CDI bean or helper class — then running Quarkus augmentation; building templates with a namespace method that isn't static.
Common situations: Migrating an older extension style to @TemplateExtension and forgetting 'static'; adding @TemplateExtension to a regular bean method; copying an example but dropping the static modifier.
Related errors
- Dev services for ${request.getName()} requires a startable s
- No template variant found
- Message bundle name [%s] declared on %s must be a valid name
- Message bundle interface name conflict - [%s] is used for bo
- @MessageBundle must be declared on an interface: {bundleClas
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/eff9f6e9334029aa.
Report an issue: GitHub.