{"record":{"id":"5fb989154e67db6e","repo":"jeecgboot/JeecgBoot","slug":"error-5fb989","errorCode":null,"errorMessage":"删除定时任务失败","messagePattern":"删除定时任务失败","errorType":"exception","errorClass":"JeecgBootException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java","lineNumber":173,"sourceCode":"\t\t\tthrow new JeecgBootException(e.getMessage(), e);\n\t\t}catch (Exception e) {\n\t\t\tthrow new JeecgBootException(\"后台找不到该类名：\" + jobClassName, e);\n\t\t}\n\t}\n\n\t/**\n\t * 删除定时任务\n\t * \n\t * @param id\n\t */\n\tprivate void schedulerDelete(String id) {\n\t\ttry {\n\t\t\tscheduler.pauseTrigger(TriggerKey.triggerKey(id));\n\t\t\tscheduler.unscheduleJob(TriggerKey.triggerKey(id));\n\t\t\tscheduler.deleteJob(JobKey.jobKey(id));\n\t\t} catch (Exception e) {\n\t\t\tlog.error(e.getMessage(), e);\n\t\t\tthrow new JeecgBootException(\"删除定时任务失败\");\n\t\t}\n\t}\n\n\t/**\n\t * 安全加载Job类：仅允许 org.jeecg. 包下的类，且必须实现 org.quartz.Job 接口\n\t */\n\tprivate static Job getClass(String classname) throws Exception {\n\t\t// 包名白名单校验，防止任意类实例化导致RCE\n\t\tif (classname == null || !classname.startsWith(\"org.jeecg.\")) {\n\t\t\tthrow new IllegalArgumentException(\"非法的任务类名：\" + classname + \"，仅允许 org.jeecg 包下的Job类\");\n\t\t}\n\t\t//update-begin---author:scott ---date:20260416  for：【PR#9538】Class.forName使用上下文类加载器，增强部署兼容性-----------\n\t\tClass<?> clazz = Class.forName(classname, true, Thread.currentThread().getContextClassLoader());\n\t\t//update-end---author:scott ---date:20260416  for：【PR#9538】Class.forName使用上下文类加载器，增强部署兼容性-----------\n\t\t// 校验是否实现了 org.quartz.Job 接口\n\t\tif (!Job.class.isAssignableFrom(clazz)) {\n\t\t\tthrow new IllegalArgumentException(\"非法的任务类：\" + classname + \"，必须实现 org.quartz.Job 接口\");\n\t\t}","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java#L155-L191","documentation":"The catch-all in QuartzJobServiceImpl.schedulerDelete fires when any exception occurs during pauseTrigger, unscheduleJob, or deleteJob. The underlying cause is only written to the server log (log.error) and is intentionally NOT included in the user-facing message, so root-cause debugging requires reading the log rather than the API response.","triggerScenarios":"Deleting a job whose TriggerKey/JobKey was already removed from the Quartz store; the QRTZ_* JDBC tables are unreachable, locked, or missing; clustered scheduler contention/timeout; passing a null or empty id (TriggerKey.triggerKey(null)).","commonSituations":"Job was already deleted directly in the DB leaving a dangling sys_quartz_job row; Quartz tables not migrated/created in a new environment; cluster misconfiguration; the scheduler is in standby/error state.","solutions":["Read the server log line written by log.error(e.getMessage(), e) to obtain the real cause.","Verify the QRTZ_* tables exist and are reachable from the configured datasource.","If the trigger is already gone, remove the dangling sys_quartz_job row manually.","Ensure the scheduler bean is started (not in standby) before issuing delete."],"exampleFix":"// before\nprivate void schedulerDelete(String id) {\n    try { scheduler.pauseTrigger(TriggerKey.triggerKey(id)); ... }\n    catch (Exception e) { log.error(e.getMessage(), e); throw new JeecgBootException(\"删除定时任务失败\"); }\n}\n// after: surface the cause for diagnostics and guard null id\nprivate void schedulerDelete(String id) {\n    if (id == null || id.isBlank()) throw new JeecgBootException(\"trigger id 不能为空\");\n    try { scheduler.pauseTrigger(TriggerKey.triggerKey(id)); ... }\n    catch (Exception e) { throw new JeecgBootException(\"删除定时任务失败: \" + e.getMessage(), e); }\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check the trigger exists before attempting delete, to avoid the catch-all.\nprivate boolean triggerExists(String id) {\n    try {\n        return scheduler.getTrigger(TriggerKey.triggerKey(id)) != null;\n    } catch (SchedulerException e) {\n        return false;\n    }\n}\nif (id == null || id.isBlank() || !triggerExists(id)) {\n    // nothing to delete; treat as success or return a clear message\n}","typeGuard":null,"tryCatchPattern":"try {\n    quartzJobService.delete(id);\n} catch (JeecgBootException e) {\n    // The real cause is in the server log (log.error in schedulerDelete).\n    log.warn(\"删除定时任务失败 id={}, 请查看后端日志获取根因\", id);\n    return Result.error(\"删除失败, 请联系管理员查看日志\");\n}","preventionTips":["Always correlate the server log timestamp with the failed delete to recover the swallowed cause.","Ensure QRTZ_* tables are part of every environment's DB setup/migration.","Avoid deleting jobs directly in the DB; use the API so sys_quartz_job and the scheduler stay in sync.","Guard null/empty ids before calling schedulerDelete."],"tags":["quartz","scheduler","cleanup","diagnostics"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}