floci-io/floci · error · AwsException

KeyGroupAlreadyExists

KeyGroupAlreadyExists

Error message

A key group with this name already exists.

What it means

CloudFrontService.validateUniqueKeyGroupName throws KeyGroupAlreadyExists (HTTP 409) when another stored key group (excluding the current id during updates) already uses the same Name. CloudFront key group names are globally unique per account, and the emulator scans the key group store on CreateKeyGroup and UpdateKeyGroup to enforce it.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/cloudfront/CloudFrontService.java:1096

                    "PreconditionFailed",
                    "The precondition in one or more request-header fields evaluated to false.",
                    412);
        }
        if (keyGroupInUse(id)) {
            throw new AwsException(
                    "ResourceInUse",
                    "Cannot delete this resource because it is in use.",
                    409);
        }
        keyGroupStore.delete(id);
    }

    private void validateUniqueKeyGroupName(String name, String excludedId) {
        boolean duplicate = keyGroupStore.scan(group -> true).stream()
                .anyMatch(group -> !Objects.equals(excludedId, group.getId())
                        && Objects.equals(name, group.getName()));
        if (duplicate) {
            throw new AwsException(
                    "KeyGroupAlreadyExists",
                    "A key group with this name already exists.",
                    409);
        }
    }

    public List<KeyGroup> listKeyGroups(String marker, int maxItems) {
        List<KeyGroup> all = new ArrayList<>(keyGroupStore.scan(k -> true));
        all.sort((a, b) -> a.getId().compareTo(b.getId()));
        return paginate(all, marker, maxItems, KeyGroup::getId);
    }

    private static void validatePublicKey(PublicKey key) {
        if (key == null
                || key.getCallerReference() == null
                || key.getCallerReference().isBlank()
                || key.getName() == null
                || key.getName().isBlank()

View on GitHub (pinned to 62ff490619)

Solutions

  1. Use a unique Name per create (suffix with UUID or run id) or delete the existing group first.
  2. On update, either keep the current Name or verify the target name is unused via ListKeyGroups.
  3. Add teardown or unique naming to fixtures so reruns cannot collide.

Example fix

// before
createKeyGroup("kg", keys); // ok
createKeyGroup("kg", otherKeys); // 409

// after
createKeyGroup("kg-" + UUID.randomUUID(), keys);
Defensive patterns

Strategy: validation

Validate before calling

boolean keyGroupNameTaken(CloudFrontClient client, String name, String excludeId) {
    return client.listKeyGroups(r -> r.build()).keyGroupList().items().stream()
            .anyMatch(g -> name.equals(g.keyGroup().name()) && !g.id().equals(excludeId));
}

Try / catch

try {
    return client.createKeyGroup(r -> r.keyGroupConfig(cfg));
} catch (KeyGroupAlreadyExists e) {
    // either reuse the existing group or retry with a suffixed unique name
}

Prevention

When it happens

Trigger: CreateKeyGroup with a Name already taken by another group; UpdateKeyGroup renaming a group to an existing name. The scan excludes the updated group's own id, so keeping your name during update is safe.

Common situations: Re-running setup scripts without teardown, so the previous run's group persists; test fixtures with fixed names like "default-kg" colliding across test classes sharing emulator state.

Related errors


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