floci-io/floci · error · AwsException

TrailAlreadyExistsException

TrailAlreadyExistsException

Error message

Trail {} already exists.

What it means

Thrown by CreateTrail when a trail with the same name already exists in the same region. Trails are keyed region:name (regionKey), and the duplicate check runs after name and bucket validation. AWS behaves the same: trail names are unique per region.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/cloudtrail/CloudTrailService.java:70

                new TypeReference<Map<String, CloudTrailEntry>>() {});
        this.regionResolver = regionResolver;
        this.iamService = iamService;
        this.mapper = mapper;
    }

    // --- Control plane ---

    public Trail createTrail(String region, String name, String s3BucketName, String s3KeyPrefix,
                             String snsTopicArn, boolean includeGlobalServiceEvents,
                             boolean isMultiRegionTrail, boolean enableLogFileValidation,
                             boolean isOrganizationTrail) {
        validateTrailName(name);
        if (s3BucketName == null || s3BucketName.isEmpty()) {
            throw new AwsException("S3BucketDoesNotExistException", "S3 bucket name is required.", 400);
        }
        String key = regionKey(region, name);
        if (store.get(key).isPresent()) {
            throw new AwsException("TrailAlreadyExistsException",
                    "Trail " + name + " already exists.", 400);
        }
        String arn = AwsArnUtils.Arn.of("cloudtrail", region, regionResolver.getAccountId(),
                "trail/" + name).toString();
        Trail trail = new Trail(
                name, arn, s3BucketName, s3KeyPrefix, snsTopicArn,
                includeGlobalServiceEvents, isMultiRegionTrail, region,
                enableLogFileValidation, false, false, isOrganizationTrail);
        store.put(key, new CloudTrailEntry(trail, List.of(), false, null, null));
        return trail;
    }

    public void deleteTrail(String region, String trailNameOrArn) {
        Trail trail = findTrailOrThrow(region, trailNameOrArn);
        store.delete(regionKey(trail.homeRegion(), trail.name()));
        pendingRecordsByTrail.keySet().removeIf(k -> k.trailName().equals(trail.name()));
    }

View on GitHub (pinned to 62ff490619)

Solutions

  1. DescribeTrails first and skip creation if the name already exists in the region.
  2. If the existing trail is stale, DeleteTrail before recreating.
  3. Make bootstrap idempotent: create-if-absent instead of unconditional create.

Example fix

// before
client.createTrail(b -> b.name("audit").s3BucketName(bucket));

// after
boolean exists = client.describeTrails().trailList().stream()
    .anyMatch(t -> t.name().equals("audit"));
if (!exists) {
    client.createTrail(b -> b.name("audit").s3BucketName(bucket));
}
Defensive patterns

Strategy: validation

Validate before calling

boolean exists = client.describeTrails().trailList().stream()
    .anyMatch(t -> t.name().equals(name) && t.homeRegion().equals(region));
if (!exists) client.createTrail(req);

Try / catch

try {
    client.createTrail(req);
} catch (TrailAlreadyExistsException e) {
    // adopt the existing trail: update its settings instead of creating
}

Prevention

When it happens

Trigger: Running CreateTrail twice with the same Name in one region — e.g. a setup script that is not idempotent, or a re-deploy after a partial failure that already created the trail.

Common situations: Bootstrap scripts run on every boot; state drift where the tool believes the trail does not exist (DescribeTrails filtering bug); parallel provisioning jobs racing to create the same trail.

Related errors


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