{"record":{"id":"efa1721d8ea62f43","repo":"floci-io/floci","slug":"resourceinuseexception","errorCode":"ResourceInUseException","errorMessage":"Certificate \" + certificateArn + \" is in use by: \" + String.join(\", \", cert.getInUseBy())","messagePattern":"Certificate \" \\+ certificateArn \\+ \" is in use by: \" \\+ String\\.join\\(\", \", cert\\.getInUseBy\\(\\)\\)","errorType":"http","errorClass":"AwsException","httpStatus":409,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java","lineNumber":314,"sourceCode":"        if (token == null || token.isEmpty()) return null;\n        try {\n            String json = new String(Base64.getDecoder().decode(token), StandardCharsets.UTF_8);\n            // Simple JSON parsing without Jackson dependency in this method\n            int start = json.indexOf(\"\\\"lastArn\\\":\\\"\") + 11;\n            int end = json.indexOf(\"\\\"\", start);\n            return json.substring(start, end);\n        } catch (Exception e) {\n            throw new AwsException(\"InvalidNextTokenException\", \"Invalid pagination token\", 400);\n        }\n    }\n\n    // ============ DeleteCertificate ============\n\n    public void deleteCertificate(String certificateArn, String region) {\n        Certificate cert = getCertificateByArn(certificateArn, region);\n\n        if (cert.getInUseBy() != null && !cert.getInUseBy().isEmpty()) {\n            throw new AwsException(\"ResourceInUseException\",\n                \"Certificate \" + certificateArn + \" is in use by: \" + String.join(\", \", cert.getInUseBy()), 409);\n        }\n\n        String storageKey = regionKey(region, cert.extractCertificateId());\n        store.delete(storageKey);\n        LOG.infov(\"Deleted certificate: {0}\", certificateArn);\n    }\n\n    // ============ ImportCertificate ============\n\n    public Certificate importCertificate(String certificatePem, String privateKeyPem, String chainPem,\n                                          String existingArn, Map<String, String> tags, String region) {\n        logSecurityWarningOnce();\n        // Parse and validate certificate\n        X509Certificate x509Cert;\n        try {\n            x509Cert = certificateGenerator.parseCertificate(certificatePem);\n            certificateGenerator.validateCertificate(x509Cert);","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java#L296-L332","documentation":"ACM ResourceInUseException (HTTP 409) thrown by AcmService.deleteCertificate when the certificate's inUseBy list is non-empty. Floci tracks which resources (e.g. load balancers, CloudFront distributions) reference a certificate; ACM refuses deletion while the certificate is attached, mirroring AWS behavior.","triggerScenarios":"acm.deleteCertificate on an ARN whose Certificate.inUseBy contains at least one entry — i.e. some other emulated resource (ALB listener, CloudFront alias, API Gateway domain) still references the cert. The message enumerates the exact holders.","commonSituations":"Test teardown deleting certificates before the stacks that use them; CloudFormation stacks still ACTIVE referencing the cert via Fn::GetAtt/Ref; retrying a cleanup script out of order after a partial failure.","solutions":["Read the error message — it lists exactly which resources hold the certificate","Delete or detach the dependent resource(s) first (delete the ALB/CloudFront/stack), then retry deleteCertificate","In CloudFormation scenarios, delete the stack that owns the certificate rather than calling deleteCertificate directly","Make cleanup scripts order-independent: loop until deletions succeed or tolerate 409 with a retry budget"],"exampleFix":"// before\nacm.deleteCertificate(r -> r.certificateArn(arn));\n\n// after: release dependents first, then delete\n// 1) delete stack / listener that references arn\n// 2) optionally confirm: describeCertificate(arn).inUseBy() is empty\nacm.deleteCertificate(r -> r.certificateArn(arn));","handlingStrategy":"try-catch","validationCode":"var cert = acm.describeCertificate(r -> r.certificateArn(arn)).certificate();\nif (cert.hasInUseBy() && !cert.inUseBy().isEmpty()) {\n    throw new IllegalStateException(\"certificate still in use by \" + cert.inUseBy());\n}","typeGuard":null,"tryCatchPattern":"try {\n    acm.deleteCertificate(r -> r.certificateArn(arn));\n} catch (ResourceInUseException e) {\n    // parse the holders from e.getMessage(), delete them first, then retry once\n}","preventionTips":["Order teardown: consumers before certificates","In CloudFormation, delete the owning stack instead of calling deleteCertificate directly","Make cleanup scripts tolerate 409 and re-run rather than aborting"],"tags":["acm","delete","conflict","resource-in-use","aws"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}