alibaba/nacos · error · NacosApiException
21009
21009
Error message
Service {service.getGroupedServiceName()} is not empty, can't be delete. Please unregister instance first What it means
Thrown by ServiceOperatorV2Impl.delete(Service) when the service singleton exists but serviceStorage.getPushData(service).getHosts() is non-empty — meaning the service still has registered instances. Error code 21009 (SERVICE_DELETE_FAILURE), NacosException.INVALID_PARAM (400). Nacos refuses to delete a service that still hosts instances to prevent orphaned references.
Source
Thrown at naming/src/main/java/com/alibaba/nacos/naming/core/ServiceOperatorV2Impl.java:134
public void delete(String namespaceId, String serviceName) throws NacosException {
Service service = getServiceFromGroupedServiceName(namespaceId, serviceName, true);
delete(service);
}
/**
* Delete service.
*
* @param service service v2
* @throws NacosException nacos exception during delete
*/
public void delete(Service service) throws NacosException {
if (!ServiceManager.getInstance().containSingleton(service)) {
throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_NOT_EXIST,
String.format("service %s not found!", service.getGroupedServiceName()));
}
if (!serviceStorage.getPushData(service).getHosts().isEmpty()) {
throw new NacosApiException(NacosException.INVALID_PARAM,
ErrorCode.SERVICE_DELETE_FAILURE,
"Service " + service.getGroupedServiceName()
+ " is not empty, can't be delete. Please unregister instance first");
}
metadataOperateService.deleteServiceMetadata(service);
}
@Override
public ObjectNode queryService(String namespaceId, String serviceName) throws NacosException {
Service service = getServiceFromGroupedServiceName(namespaceId, serviceName, true);
if (!ServiceManager.getInstance().containSingleton(service)) {
throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SERVICE_NOT_EXIST,
"service not found, namespace: " + namespaceId + ", serviceName: " + serviceName);
}
ObjectNode result = JacksonUtils.createEmptyJsonNode();
ServiceMetadata serviceMetadata =
metadataManager.getServiceMetadata(service).orElse(new ServiceMetadata());
setServiceMetadata(result, serviceMetadata, service);View on GitHub (pinned to 9b989acdf1)
Solutions
- Deregister all instances for the service first (DELETE instance API or gRPC deregister), then retry delete.
- Stop all client processes that register instances against this service, then wait for ephemeral instances to expire.
- For persistent instances, ensure the CP/Raft log entry is removed via the deregister path (not just in-memory).
- Verify the host list is empty via selectInstances before calling delete.
Example fix
// before
serviceOperatorV2.delete(namespaceId, serviceName);
// after — drain instances first
ServiceInfo info = serviceStorage.getData(service);
for (Instance h : info.getHosts()) {
instanceOperator.deregisterInstance(namespaceId, group, name,
h.getIp(), h.getPort(), h.getClusterName());
}
// wait for propagation, then
serviceOperatorV2.delete(namespaceId, serviceName); Defensive patterns
Strategy: validation
Validate before calling
ServiceInfo info = serviceStorage.getData(service);
if (!info.getHosts().isEmpty()) {
// drain instances first
for (Instance h : info.getHosts()) {
instanceOperator.deregisterInstance(namespaceId, group, name,
h.getIp(), h.getPort(), h.getClusterName());
}
// wait for propagation
}
serviceOperatorV2.delete(service); Try / catch
try {
serviceOperatorV2.delete(service);
} catch (NacosApiException e) {
if (e.getErrCode() == NacosException.INVALID_PARAM
&& ErrorCode.SERVICE_DELETE_FAILURE.getCode() == 21009) {
// service still has instances — drain and retry
} else throw e;
} Prevention
- Always deregister all instances before deleting a service.
- Stop client processes that re-register ephemeral instances before cleanup.
- Verify the host list is empty before calling delete.
When it happens
Trigger: Calling delete-service while one or more instances are still registered under the service. The instances may be ephemeral (still being heartbeated by clients) or persistent (stored in the CP log). Even unhealthy instances count as long as they are in the host list.
Common situations: Decommissioning a service without first deregistering all instances. Clients are still running and re-registering ephemeral instances faster than they can be removed. Persistent instances remain in the Raft log. A stale client keeps the instance alive via heartbeats.
Related errors
- 10000
- 400
- Param 'serviceName' is illegal, serviceName is blank
- Param 'groupName' is illegal, groupName is blank
- Param 'serviceName' is illegal, it should be format as 'grou
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/469c0104a4546d90.
Report an issue: GitHub.