{"record":{"id":"fe3574676c8166e5","repo":"alibaba/canal","slug":"instance","errorCode":null,"errorMessage":"当前集群下存在Instance配置，无法删除","messagePattern":"当前集群下存在Instance配置，无法删除","errorType":"validation","errorClass":"ServiceException","httpStatus":null,"severity":"error","filePath":"admin/admin-web/src/main/java/com/alibaba/otter/canal/admin/service/impl/CanalClusterServiceImpl.java","lineNumber":40,"sourceCode":"    public CanalCluster detail(Long id) {\n        return CanalCluster.find.byId(id);\n    }\n\n    public void update(CanalCluster canalCluster) {\n        canalCluster.update(\"name\", \"zkHosts\");\n    }\n\n    public void delete(Long id) {\n        // 判断集群下是否存在server信息\n        int serverCnt = NodeServer.find.query().where().eq(\"clusterId\", id).findCount();\n        if (serverCnt > 0) {\n            throw new ServiceException(\"当前集群下存在Server, 无法删除\");\n        }\n\n        // 判断集群下是否存在instance信息\n        int instanceCnt = CanalInstanceConfig.find.query().where().eq(\"clusterId\", id).findCount();\n        if (instanceCnt > 0) {\n            throw new ServiceException(\"当前集群下存在Instance配置，无法删除\");\n        }\n\n        CanalCluster canalCluster = CanalCluster.find.byId(id);\n        if (canalCluster != null) {\n            canalCluster.delete();\n        }\n    }\n\n    public List<CanalCluster> findList(CanalCluster canalCluster) {\n        Query<CanalCluster> query = CanalCluster.find.query();\n        query.order().asc(\"id\");\n        return query.findList();\n    }\n}\n","sourceCodeStart":22,"sourceCodeEnd":55,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/admin/admin-web/src/main/java/com/alibaba/otter/canal/admin/service/impl/CanalClusterServiceImpl.java#L22-L55","documentation":"Thrown by CanalClusterServiceImpl.delete(id) when no servers remain but CanalInstanceConfig rows still reference clusterId. The second pre-delete guard blocks deletion to avoid orphan instance configs.","triggerScenarios":"DELETE a cluster after removing its servers, but instance configs still carry clusterId == id. The instance count check throws 'cluster has Instance config(s), cannot delete'.","commonSituations":"Instance configs not cleaned up when their server/cluster was removed; instances migrated but clusterId not updated; manual DB edits leaving dangling references.","solutions":["Delete or reassign all CanalInstanceConfig rows whose clusterId == id before deleting the cluster.","Re-run the cluster delete once the instance count is zero.","Audit for orphan instance configs via SELECT count(*) FROM canal_instance_config WHERE cluster_id = <id>.","Use the instance admin UI/API to remove instances rather than direct DB deletion."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"int instanceCnt = CanalInstanceConfig.find.query().where().eq(\"clusterId\", clusterId).findCount();\nif (instanceCnt > 0) {\n    throw new IllegalStateException(\"Cannot delete cluster: \" + instanceCnt + \" instance config(s) still attached\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remove/reassign all instance configs before deleting the cluster.","Use the admin API (not raw DB deletes) to clean instances.","Run referential-integrity checks in a pre-delete hook."],"tags":["canal-admin","cluster","referential-integrity","delete-guard"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}