{"record":{"id":"66ab1c54e9f18f0d","repo":"quarkusio/quarkus","slug":"non-static-scheduled-methods-may-not-be-declared","errorCode":null,"errorMessage":"Non-static @Scheduled methods may not be declared on abstract classes and interfaces: %s() declared on %s","messagePattern":"Non-static @Scheduled methods may not be declared on abstract classes and interfaces: (.+?)\\(\\) declared on (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java","lineNumber":204,"sourceCode":"        Map<MethodInfo, List<AnnotationInstance>> staticScheduledMethods = new HashMap<>();\n        List<AnnotationInstance> schedules = new ArrayList<>(\n                beanArchives.getIndex().getAnnotations(SchedulerDotNames.SCHEDULED_NAME));\n        for (AnnotationInstance annotationInstance : beanArchives.getIndex().getAnnotations(SchedulerDotNames.SCHEDULES_NAME)) {\n            for (AnnotationInstance scheduledInstance : annotationInstance.value().asNestedArray()) {\n                // We need to set the target of the containing instance\n                schedules.add(AnnotationInstance.create(scheduledInstance.name(), annotationInstance.target(),\n                        scheduledInstance.values()));\n            }\n        }\n        for (AnnotationInstance annotationInstance : schedules) {\n            if (annotationInstance.target().kind() != METHOD) {\n                continue; // This should never happen as the annotation has @Target(METHOD)\n            }\n            MethodInfo method = annotationInstance.target().asMethod();\n            ClassInfo declaringClass = method.declaringClass();\n            if (!Modifier.isStatic(method.flags())\n                    && (Modifier.isAbstract(declaringClass.flags()) || declaringClass.isInterface())) {\n                throw new IllegalStateException(String.format(\n                        \"Non-static @Scheduled methods may not be declared on abstract classes and interfaces: %s() declared on %s\",\n                        method.name(), declaringClass.name()));\n            }\n            if (Modifier.isStatic(method.flags()) && !KotlinUtil.isSuspendMethod(method)) {\n                List<AnnotationInstance> methodSchedules = staticScheduledMethods.get(method);\n                if (methodSchedules == null) {\n                    methodSchedules = new ArrayList<>();\n                    staticScheduledMethods.put(method, methodSchedules);\n                }\n                methodSchedules.add(annotationInstance);\n            }\n        }\n\n        for (Entry<MethodInfo, List<AnnotationInstance>> e : staticScheduledMethods.entrySet()) {\n            MethodInfo method = e.getKey();\n            scheduledBusinessMethods.produce(new ScheduledBusinessMethodItem(null, method, e.getValue(),\n                    transformedAnnotations.hasAnnotation(method, SchedulerDotNames.NON_BLOCKING),\n                    transformedAnnotations.hasAnnotation(method, SchedulerDotNames.RUN_ON_VIRTUAL_THREAD)));","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java#L186-L222","documentation":"@Scheduled methods must be either static or declared on a concrete (non-abstract, non-interface) class so the scheduler can instantiate/invoke them. During index scanning, collectScheduledMethods rejects non-static methods found on abstract classes or interfaces at build time.","triggerScenarios":"Declaring a non-static @Scheduled method inside an interface or an abstract class that is not overridden by a concrete bean at build time.","commonSituations":"Putting scheduled job signatures in a shared interface expecting subclasses to inherit scheduling; base abstract class with a @Scheduled template method; refactoring a concrete scheduled class into an abstract hierarchy.","solutions":["Move the @Scheduled method to a concrete class with a CDI scope","Make the method static if it must live on an abstract type","Remove @Scheduled from interfaces; schedule in concrete implementations instead","Have each concrete subclass declare its own @Scheduled method"],"exampleFix":"// before\npublic interface Jobs {\n    @Scheduled(every = \"10s\")\n    void run(); // non-static on interface\n}\n// after\n@ApplicationScoped\npublic class Jobs {\n    @Scheduled(every = \"10s\")\n    void run() { ... }\n}","handlingStrategy":"validation","validationCode":"static boolean isLegalScheduledMethod(Method m) {\n    int cls = m.getDeclaringClass().getModifiers();\n    return java.lang.reflect.Modifier.isStatic(m.getModifiers())\n        || (!java.lang.reflect.Modifier.isAbstract(cls) && !m.getDeclaringClass().isInterface());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Put @Scheduled only on concrete, CDI-scoped classes","Use static scheduled methods for abstract holders","Never place @Scheduled on interface methods","Review scheduled classes when refactoring into hierarchies"],"tags":["scheduler","build-time","annotations","quarkus"],"backgroundTag":"invalid-scheduled-method-declaration","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"}