xuxueli/xxl-job · error · RuntimeException
xxl-job method-jobhandler destroyMethod invalid, for[{}#{}]
Error message
xxl-job method-jobhandler destroyMethod invalid, for[{}#{}] . What it means
Thrown by registryJobHandler when @XxlJob(destroy="...") names a method not found on the bean's class (NoSuchMethodException rewrapped). The destroy lifecycle hook must be a declared no-arg method on the same class.
Source
Thrown at xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java:376
// init and destroy
Method initMethod = null;
Method destroyMethod = null;
if (StringTool.isNotBlank(xxlJob.init())) {
try {
initMethod = clazz.getDeclaredMethod(xxlJob.init());
initMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + clazz + "#" + methodName + "] .");
}
}
if (StringTool.isNotBlank(xxlJob.destroy())) {
try {
destroyMethod = clazz.getDeclaredMethod(xxlJob.destroy());
destroyMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + clazz + "#" + methodName + "] .");
}
}
// registry jobhandler
registryJobHandler(name, new MethodJobHandler(bean, executeMethod, initMethod, destroyMethod));
}
// ---------------------- job thread repository ----------------------
private final ConcurrentMap<Integer, JobThread> jobThreadRepository = new ConcurrentHashMap<>();
public JobThread registJobThread(int jobId, IJobHandler handler, String removeOldReason){
JobThread newJobThread = new JobThread(jobId, handler);
newJobThread.start();
logger.info(">>>>>>>>>>> xxl-job register JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler});
JobThread oldJobThread = jobThreadRepository.put(jobId, newJobThread); // putIfAbsent | oh my god, map's put method return the old value!!!View on GitHub (pinned to e74c784f68)
Solutions
- Declare a no-arg method on the same class matching @XxlJob(destroy=...).
- Verify spelling and visibility of the destroy method.
- Drop destroy="..." if no teardown is required.
Example fix
// before
@XxlJob(value="x", destroy="cleanup")
public ReturnT<String> execute(String p){...}
// cleanup() missing
// after
@XxlJob(value="x", destroy="cleanup")
public ReturnT<String> execute(String p){...}
public void cleanup(){ /* release */ } Defensive patterns
Strategy: validation
Validate before calling
if (StringTool.isNotBlank(xxlJob.destroy())) {
Method dm = clazz.getDeclaredMethod(xxlJob.destroy());
if (dm.getParameterCount() != 0) throw new IllegalStateException("destroy must be no-arg");
} Try / catch
try {
xxlJobExecutor.start();
} catch (RuntimeException e) {
log.error("handler destroy method invalid: {}", e.getMessage());
} Prevention
- Keep destroy methods no-arg and on the annotated class.
- Sync annotation text when renaming cleanup methods.
- Reflect-test all @XxlJob init/destroy references at build time.
When it happens
Trigger: A handler annotated @XxlJob(value="x", destroy="tearDown") where tearDown is absent, has parameters, sits on a superclass, or was renamed.
Common situations: Renamed the cleanup method without updating the annotation; declared it with arguments; method exists only on a parent class invisible to getDeclaredMethod.
Related errors
- xxl-job method-jobhandler initMethod invalid, for[{}#{}] .
- xxl-job method-jobhandler name invalid, for[{}#{}] .
- xxl-job jobhandler[{}] naming conflicts.
- >>>>>>>>>>> xxl-glue, loadNewInstance error, cannot convert
- >>>>>>>>>>> xxl-job load executor instance fail, please init
AI-assisted analysis of xuxueli/xxl-job@e74c784f68 (2026-08-14).
Data as JSON: /api/errors/2b1865187bae5bd1.
Report an issue: GitHub.