floci-io/floci · error · AwsException

ValidationError

ValidationError

Error message

Updating RoleName requires resource replacement, which is not supported.

What it means

ValidationError from the AWS::IAM::Role provisioner when an update resolves a different RoleName than the one recorded at create time. RoleName is a replacement property in AWS and the emulator does not model replacement here, so a mismatch is rejected instead of creating a second role.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/IamRoleCfnProvisioner.java:58

    }

    @Override
    public Set<String> resourceTypes() {
        return Set.of("AWS::IAM::Role");
    }

    @Override
    public void provision(StackResource r, JsonNode props, ProvisionContext ctx) {
        String existingRoleName = r.getPhysicalId();
        String roleName = ctx.resolveOptional(props, "RoleName");
        if (roleName == null || roleName.isBlank()) {
            roleName = existingRoleName != null && !existingRoleName.isBlank()
                    ? existingRoleName
                    : ctx.generatePhysicalName(r.getLogicalId(), 64, false);
        }
        final String resolvedRoleName = roleName;
        if (existingRoleName != null && !existingRoleName.equals(resolvedRoleName)) {
            throw new AwsException("ValidationError",
                    "Updating RoleName requires resource replacement, which is not supported.", 400);
        }
        String assumeDoc = props != null && props.has("AssumeRolePolicyDocument")
                ? props.get("AssumeRolePolicyDocument").toString()
                : "{\"Version\":\"2012-10-17\",\"Statement\":[]}";
        String path = ctx.resolveOptional(props, "Path");
        if (path == null) {
            path = "/";
        }
        String description = ctx.resolveOptional(props, "Description");
        List<String> managedPolicyArns = ctx.resolveStringList(props, "ManagedPolicyArns");

        IamRole role;
        boolean createdRole = false;
        // Set only on the adoption path, to the role's trust policy (and verified RoleId) as they
        // were before this attempt touched them. Lets a later failure (e.g. bad ManagedPolicyArns)
        // restore the trust policy below, so a rolled-back update doesn't leave it changed despite
        // UPDATE_ROLLBACK_COMPLETE - and restore it onto the *same verified role*, not onto a

View on GitHub (pinned to 62ff490619)

Solutions

  1. Keep RoleName identical across updates, or omit it in every version of the template.
  2. If a rename is required, treat it as replacement: delete and recreate the stack or move the role to a new logical id.
  3. Note the same asymmetry as other named resources: once created unnamed, the generated name is sticky and adding a declared name always fails.

Example fix

# before (v1 had no RoleName, v2 adds one)
Type: AWS::IAM::Role
Properties:
  RoleName: app-role   # != generated physical id

# after (keep consistent across versions)
Type: AWS::IAM::Role
Properties:
  # no RoleName in any version; or set it in v1 from the start
  AssumeRolePolicyDocument: ...
Defensive patterns

Strategy: validation

Validate before calling

String declared = template.stringAt("Resources/Role/Properties/RoleName");
String recorded = describeStackResource(stack, "Role").physicalResourceId();
if (declared != null && !declared.equals(recorded)) {
    throw new IllegalStateException("RoleName change requires stack recreation");
}

Try / catch

catch ValidationError "Updating RoleName requires resource replacement": revert RoleName in the template, or recreate (new logical id / new stack); the update itself can never succeed.

Prevention

When it happens

Trigger: UpdateStack where the template's RoleName differs from the stack resource's physical id — adding a RoleName to a role that was originally created unnamed (generated name), or editing the declared name.

Common situations: Templates versioned over time that introduce or rename RoleName; environments where a role first deployed without an explicit name is later tightened to carry one.

Understand the failure class

Background: ValidationError explained: why open-source libraries reject your input — file uploads, YAML manifests, unique fields, and query permissions — this error's family across 13 libraries.

Related errors


AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14). Data as JSON: /api/errors/f637fb6b6bd82a18. Report an issue: GitHub.