{"record":{"id":"c6d73b31e8a26957","repo":"floci-io/floci","slug":"validationerror-c6d73b","errorCode":"ValidationError","errorMessage":"Updating Name requires resource replacement, which is not supported.","messagePattern":"Updating Name requires resource replacement, which is not supported\\.","errorType":"validation","errorClass":"AwsException","httpStatus":400,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/EcsCapacityCfnProvisioner.java","lineNumber":88,"sourceCode":"            // providers attached, which is what AWS does: the associations are the resource.\n            case ASSOCIATIONS -> clearAssociations(physicalId, region);\n            default -> { }\n        }\n    }\n\n    private void provisionCapacityProvider(StackResource r, JsonNode props, ProvisionContext ctx) {\n        String existingName = r.getPhysicalId();\n        String declaredName = ctx.resolveOptional(props, \"Name\");\n        // An unnamed provider keeps the name its first execution generated. Minting a fresh one on\n        // every pass left the previous provider behind with nothing referencing it.\n        String name = declaredName != null && !declaredName.isBlank()\n                ? declaredName\n                : (existingName != null && !existingName.isBlank()\n                        ? existingName\n                        : ctx.generatePhysicalName(r.getLogicalId(), 255, false));\n\n        if (existingName != null && !existingName.isBlank() && !existingName.equals(name)) {\n            throw new AwsException(\"ValidationError\",\n                    \"Updating Name requires resource replacement, which is not supported.\", 400);\n        }\n\n        Map<String, Object> asgProvider = asgProvider(props, ctx);\n        Map<String, String> tags = tags(props, ctx);\n\n        // UpdateStack re-executes every resource with the physical id it got at create time, and\n        // createCapacityProvider rejects a name that already exists, so an unchanged provider used\n        // to fail the whole update. An empty result also covers a provider removed out of band,\n        // which is recreated rather than reported as a failure.\n        CapacityProvider existing = existingName == null || existingName.isBlank()\n                ? null\n                : ecsService.describeCapacityProviders(List.of(existingName)).stream()\n                        .findFirst().orElse(null);\n\n        if (existing == null) {\n            ecsService.createCapacityProvider(name, asgProvider, tags, ctx.region());\n        } else {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/EcsCapacityCfnProvisioner.java#L70-L106","documentation":"ValidationError from the AWS::ECS::CapacityProvider provisioner when an update changes the provider's Name. Name is create-only in this implementation (replacement is not modeled), and the guard exists because a name change would otherwise mint a new provider and orphan the old one with nothing referencing it.","triggerScenarios":"UpdateStack on a stack containing AWS::ECS::CapacityProvider where the template's Name property differs from the physical id recorded at create time — including switching from an unnamed provider to a named one.","commonSituations":"Renaming a capacity provider in a template between deploys, or adding a Name to a resource that was originally created without one (generated name) so the declared name never equals the stored one.","solutions":["Keep the Name property identical across updates, or omit it in both the original and updated template.","If a rename is genuinely needed, delete and recreate the stack (or remove and re-add the resource) — replacement semantics.","Beware the asymmetry: a resource first created unnamed keeps its generated name forever; adding a Name later will always trip this guard."],"exampleFix":"# before (v1 omitted Name, v2 adds it)\nType: AWS::ECS::CapacityProvider\nProperties:\n  Name: prod-cp   # conflicts with generated name from v1\n\n# after (v2 keeps it unnamed, matching v1)\nType: AWS::ECS::CapacityProvider\nProperties:\n  AutoScalingGroupProvider: ...","handlingStrategy":"validation","validationCode":"// Before UpdateStack: declared Name must equal the recorded physical id\nString declared = template.stringAt(\"Resources/Cap/Properties/Name\");\nString recorded = describeStackResource(stack, \"Cap\").physicalResourceId();\nif (declared != null && !declared.equals(recorded)) {\n    throw new IllegalStateException(\"Name change requires stack recreation\");\n}","typeGuard":null,"tryCatchPattern":"catch ValidationError \"Updating Name requires resource replacement\" during UpdateStack: either revert the Name in the template or delete/recreate the stack (or move the resource to a new logical id); retrying the same template always fails.","preventionTips":["Decide the capacity provider's Name in v1 of the template and never change it","Omit Name entirely if you do not need a stable one — but then never add it later","Treat name changes as delete+create operations in deployment tooling"],"tags":["cloudformation","ecs","capacity-provider","replacement"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}