{"record":{"id":"87ad2a7ec046d846","repo":"floci-io/floci","slug":"resourceinuse-87ad2a","errorCode":"ResourceInUse","errorMessage":"The namespace contains existing services and cannot be deleted.","messagePattern":"The namespace contains existing services and cannot be deleted\\.","errorType":"exception","errorClass":"AwsException","httpStatus":400,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/services/cloudmap/CloudMapService.java","lineNumber":149,"sourceCode":"        return requireNamespace(id);\n    }\n\n    public List<Namespace> listNamespaces(String region) {\n        List<Namespace> result = new ArrayList<>();\n        for (Namespace n : scan(namespaceStore)) {\n            if (region.equals(n.getRegion())) {\n                result.add(n);\n            }\n        }\n        return result;\n    }\n\n    public Operation deleteNamespace(String id, String region) {\n        Namespace ns = requireNamespace(id);\n        boolean hasServices = scan(serviceStore).stream()\n                .anyMatch(s -> ns.getId().equals(s.getNamespaceId()));\n        if (hasServices) {\n            throw new AwsException(\"ResourceInUse\",\n                    \"The namespace contains existing services and cannot be deleted.\", 400);\n        }\n        namespaceStore.delete(id);\n        return submitOperation(\"DELETE_NAMESPACE\", \"NAMESPACE\", id, region);\n    }\n\n    // ──────────────────────────── Services ────────────────────────────\n\n    public Service createService(String name, String namespaceId, String creatorRequestId,\n                                 String description, String dnsConfig, String healthCheckConfig,\n                                 String healthCheckCustomConfig, String type,\n                                 Map<String, String> tags, String region) {\n        if (name == null || name.isBlank()) {\n            throw new AwsException(\"InvalidInput\", \"Service name is required.\", 400);\n        }\n        String resolvedNamespaceId = namespaceId;\n        if (dnsConfig != null && resolvedNamespaceId == null) {\n            // DnsConfig may carry the namespace id when the top-level field is absent.","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/services/cloudmap/CloudMapService.java#L131-L167","documentation":"A Cloud Map namespace cannot be deleted while any service still references it. Floci's deleteNamespace scans the service store for services whose namespaceId equals the namespace id and throws ResourceInUse (HTTP 400) when at least one is found. AWS returns the same error; deletion is a no-op until all services in the namespace are deleted first.","triggerScenarios":"DeleteNamespace on a namespace that contains services created via CreateService. Typical in teardown scripts that delete the namespace without enumerating and deleting its services, or when a service creation raced the delete.","commonSituations":"Environment teardown ordering bugs (namespace deleted before its services); test cleanup between runs; a service created asynchronously by another process just before deletion.","solutions":["List services with ListServices (namespace-scoped) and DeleteService each one, waiting for the delete operations to succeed, before DeleteNamespace","Make teardown order explicit: services first, then namespace","Catch ResourceInUse and retry after the pending service deletions complete"],"exampleFix":"// before\ncloudMapClient.deleteNamespace(r -> r.id(namespaceId));\n\n// after\ncloudMapClient.listServices(r -> r.namespaceId ? null : null); // enumerate\nList<ServiceSummary> services = new ArrayList<>();\nString nextToken = null;\ndo {\n    final String token = nextToken;\n    ListServicesResponse page = cloudMapClient.listServices(r -> {\n        if (token != null) r.nextToken(token);\n    });\n    services.addAll(page.services());\n    nextToken = page.nextToken();\n} while (nextToken != null);\nfor (ServiceSummary s : services) {\n    if (namespaceId.equals(s.namespaceId())) {\n        cloudMapClient.deleteService(r -> r.id(s.id()));\n    }\n}\ncloudMapClient.deleteNamespace(r -> r.id(namespaceId));","handlingStrategy":"validation","validationCode":"boolean hasServices = cloudMapClient.listServices Paginator.stream(r -> r)\n    .flatMap(p -> p.services().stream())\n    .anyMatch(s -> namespaceId.equals(s.namespaceId()));\nif (hasServices) {\n    // delete each service (and wait for the delete operation) before DeleteNamespace\n}","typeGuard":null,"tryCatchPattern":"try {\n    cloudMapClient.deleteNamespace(r -> r.id(namespaceId));\n} catch (ServiceDiscoveryException e) {\n    if (\"ResourceInUse\".equals(e.awsErrorDetails().errorCode())) {\n        // delete remaining services, wait for their operations, then retry the namespace delete\n    } else { throw e; }\n}","preventionTips":["Enforce teardown order: services first, namespace last","Wait for service delete operations to complete before deleting the namespace"],"tags":["cloudmap","namespace","delete","dependent-resources"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}