apache/shenyu · error · ShenyuException
shenyu this discovery has discoveryHandler can't be delete
Error message
shenyu this discovery has discoveryHandler can't be delete
What it means
DiscoveryServiceImpl.delete() refuses to delete a discovery config that still has discovery handler rows attached: if discoveryHandlerMapper.selectByDiscoveryId() returns rows, it throws ShenyuException "shenyu this discovery has discoveryHandler can't be delete". Handlers (proxy selector bindings) reference the discovery, so deleting it would leave dangling bindings; the handlers must be removed first.
Solutions
- Delete the discovery handlers referencing this discovery first (via the dashboard or the discovery-handler delete API), then retry the discovery delete.
- Identify dependent selectors/proxy bindings and remove or re-point them before deleting.
- If the handlers are orphaned, clean up the discovery_handler rows in the DB, then retry.
Example fix
// before
DELETE /discovery/{discoveryId}?namespaceId=ns1 // throws: has discoveryHandler
// after
DELETE /discovery/handler/{handlerId}?namespaceId=ns1 // remove handlers first
DELETE /discovery/{discoveryId}?namespaceId=ns1 // now succeeds Defensive patterns
Strategy: try-catch
Validate before calling
// before deleting, check for dependent handlers
List<DiscoveryHandlerDO> handlers = discoveryHandlerMapper.selectByDiscoveryId(discoveryId);
if (CollectionUtils.isNotEmpty(handlers)) {
handlers.forEach(h -> discoveryHandlerService.delete(h.getId(), namespaceId));
}
discoveryService.delete(discoveryId, namespaceId); Try / catch
try {
discoveryService.delete(discoveryId, namespaceId);
} catch (ShenyuException e) {
if (e.getMessage().contains("discoveryHandler can't be delete")) {
// delete bound discovery handlers first, then retry
}
throw e;
} Prevention
- Delete child resources (discovery handlers / proxy selector bindings) before the parent discovery.
- In cleanup scripts, iterate handlers per discoveryId before removing the discovery.
- Check the dashboard for bound selectors before deleting a discovery config.
When it happens
Trigger: Calling DELETE /discovery/{id} (with a matching namespaceId) while the discovery still has discovery handlers bound via bindingDiscovery or dashboard-created handler records; cascading cleanup that removes discoveries before handlers.
Common situations: Users deleting a discovery from the dashboard while a proxy selector still points at it; scripts cleaning up config in the wrong order; stale handlers left behind after a selector was removed out-of-band.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- before start ProxySelector you need init DiscoveryId=
- before start ProxySelector you need init DiscoveryId=
- shenyu discovery start watcher need you has this key
- shenyu discovery mode current didn't support
- when binding discovery don't find selector
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/7bfcfa6653999b47.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java:196
.id(UUIDUtils.getInstance().generateShortUuid())
.discoveryHandlerId(discoveryHandlerDO.getId())
.selectorId(selectorDO.getId())
.pluginName(discoveryConfigRegisterDTO.getPluginName()).build();
discoveryRelMapper.insertSelective(discoveryRefDO);
discoveryHandlerMapper.insertSelective(discoveryHandlerDO);
}
DiscoveryProcessor discoveryProcessor = discoveryProcessorHolder.chooseProcessor(discoveryConfigRegisterDTO.getDiscoveryType());
discoveryProcessor.createDiscovery(discoveryDO);
discoveryProcessor.createProxySelector(DiscoveryTransfer.INSTANCE.mapToDTO(discoveryHandlerDO), proxySelectorDTO);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String delete(final String discoveryId, final String namespaceId) {
List<DiscoveryHandlerDO> discoveryHandlerDOS = discoveryHandlerMapper.selectByDiscoveryId(discoveryId);
if (CollectionUtils.isNotEmpty(discoveryHandlerDOS)) {
LOG.warn("shenyu this discovery has discoveryHandler can't be delete");
throw new ShenyuException("shenyu this discovery has discoveryHandler can't be delete");
}
DiscoveryDO discoveryDO = discoveryMapper.selectById(discoveryId);
if (Objects.isNull(discoveryDO) || !Objects.equals(discoveryDO.getNamespaceId(), namespaceId)) {
LOG.warn("shenyu discovery {} is not found in namespace {}", discoveryId, namespaceId);
throw new ShenyuException("shenyu this discovery is not found in current namespace");
}
DiscoveryProcessor discoveryProcessor = discoveryProcessorHolder.chooseProcessor(discoveryDO.getDiscoveryType());
discoveryProcessor.removeDiscovery(discoveryDO);
discoveryMapper.delete(discoveryId, namespaceId);
return ShenyuResultMessage.DELETE_SUCCESS;
}
private DiscoveryVO create(final DiscoveryDTO discoveryDTO) {
if (Objects.isNull(discoveryDTO)) {
return null;
}
Timestamp currentTime = new Timestamp(System.currentTimeMillis());
DiscoveryDO discoveryDO = DiscoveryDO.builder()View on GitHub (pinned to 567142e072)