{"record":{"id":"2d1954a8c042f127","repo":"quarkusio/quarkus","slug":"a-build-step-must-be-a-non-static-method-s","errorCode":null,"errorMessage":"A build step must be a non-static method: %s","messagePattern":"A build step must be a non-static method: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java","lineNumber":433,"sourceCode":"            }\n        }\n\n        // get class-level configuration, if any\n        final BuildSteps buildSteps = clazz.getAnnotation(BuildSteps.class);\n        final Class<? extends BooleanSupplier>[] classOnlyIf = buildSteps == null ? EMPTY_BOOLEAN_SUPPLIER_CLASS_ARRAY\n                : buildSteps.onlyIf();\n        final Class<? extends BooleanSupplier>[] classOnlyIfNot = buildSteps == null ? EMPTY_BOOLEAN_SUPPLIER_CLASS_ARRAY\n                : buildSteps.onlyIfNot();\n\n        // now iterate the methods\n        final List<Method> methods = nonAbstractBuildStepMethods(clazz);\n        final Map<String, List<Method>> nameToMethods = methods.stream().collect(Collectors.groupingBy(Method::getName));\n\n        MethodHandles.Lookup lookup = MethodHandles.publicLookup();\n        for (Method method : methods) {\n            final BuildStep buildStep = method.getAnnotation(BuildStep.class);\n            if (Modifier.isStatic(method.getModifiers())) {\n                throw new RuntimeException(\"A build step must be a non-static method: \" + method);\n            }\n            if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {\n                method.setAccessible(true);\n            }\n            final Class<? extends BooleanSupplier>[] onlyIf = buildStep.onlyIf();\n            final Class<? extends BooleanSupplier>[] onlyIfNot = buildStep.onlyIfNot();\n            final Parameter[] methodParameters = method.getParameters();\n            final Record recordAnnotation = method.getAnnotation(Record.class);\n            final boolean isRecorder = recordAnnotation != null;\n            final boolean identityComparison = !isRecorder || recordAnnotation.useIdentityComparisonForParameters();\n            if (isRecorder) {\n                boolean recorderFound = false;\n                for (Class<?> p : method.getParameterTypes()) {\n                    if (isRecorder(p)) {\n                        recorderFound = true;\n                        break;\n                    }\n                }","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java#L415-L451","documentation":"ExtensionLoader.loadStepsFromClass rejects any @BuildStep-annotated method that is static, throwing 'A build step must be a non-static method: <method>'. Build steps are instantiated and invoked reflectively on the processor instance, so static methods cannot be build steps. This is a programming error in an extension's build-time processor.","triggerScenarios":"An extension author annotates a static method with @BuildStep inside a class listed in META-INF/quarkus-build-steps.list; during loadStepsFromClass the check Modifier.isStatic(method.getModifiers()) fails and throws.","commonSituations":"Writing a new build step and following the habit of static utility methods; refactoring an existing build step to static to silence an IDE warning; copying a method into a helper class while keeping @BuildStep.","solutions":["Remove the static modifier from the @BuildStep method","Move the logic to a private instance helper method and call it from the non-static @BuildStep method","If a static utility is genuinely needed, keep @BuildStep as a thin non-static wrapper that delegates to it"],"exampleFix":"// before\n@BuildStep\nstatic void produceThing(BuildProducer<MyBuildItem> producer) { ... }\n// after\n@BuildStep\nvoid produceThing(BuildProducer<MyBuildItem> producer) { ... }","handlingStrategy":"type-guard","validationCode":"// Scan processor classes for static @BuildStep methods before registering\nfor (Method m : clazz.getDeclaredMethods()) {\n    if (m.isAnnotationPresent(BuildStep.class) && Modifier.isStatic(m.getModifiers())) {\n        throw new IllegalStateException(\"@BuildStep must not be static: \" + m);\n    }\n}","typeGuard":"boolean isValidBuildStep(Method m) {\n    return m.isAnnotationPresent(BuildStep.class) && !Modifier.isStatic(m.getModifiers());\n}","tryCatchPattern":"try {\n    ExtensionLoader.loadStepsFrom(classLoader, bsf);\n} catch (RuntimeException e) {\n    throw new IllegalStateException(\"Remove static modifier on the @BuildStep method named in: \" + e.getMessage(), e);\n}","preventionTips":["Never mark @BuildStep methods static — build steps are invoked on processor instances","Use an ArchUnit/test rule to forbid static @BuildStep methods in your extension","Review IDE refactorings that add static to annotated methods","Check extension behavior by running its deployment-module tests before releasing"],"tags":["quarkus","build-steps","extension","api-misuse"],"backgroundTag":"static-build-step-method","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}