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
- DescribeTrails first and skip creation if the name already exists in the region.
- If the existing trail is stale, DeleteTrail before recreating.
- 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
- Make trail provisioning idempotent with describe-then-create.
- On partial setup failures, check DescribeTrails before re-running bootstrap.
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
- ServiceAlreadyExists
- ResourceInUseException
- IdempotencyException
- ConflictException
- PublicKeyAlreadyExists
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/b630fbd179a3165a.
Report an issue: GitHub.