floci-io/floci · error · AwsException
ConflictException
ConflictException
Error message
API with id '{apiId}' already exists What it means
Thrown by ApiGatewayV2Service.createApi() when the API id that will be assigned already exists in the same region. Normally ids are freshly generated (shortId(10)), so collisions are effectively impossible; the realistic trigger is ReservedTags.extractOverrideApiId(tags) — a reserved tag used to pin a deterministic API id — supplying an id that is already in apiStore. The pre-existence check then fails with ConflictException, HTTP 409, matching AWS behavior for id conflicts.
Source
Thrown at src/main/java/io/github/hectorvent/floci/services/apigatewayv2/ApiGatewayV2Service.java:90
if ("WEBSOCKET".equals(protocolType) && (routeSelectionExpression == null || routeSelectionExpression.isBlank())) {
throw new AwsException("BadRequestException",
"RouteSelectionExpression is required for WEBSOCKET protocol", 400);
}
// Apply AWS defaults
if (apiKeySelectionExpression == null) {
apiKeySelectionExpression = "$request.header.x-api-key";
}
if ("HTTP".equals(protocolType) && routeSelectionExpression == null) {
routeSelectionExpression = "${request.method} ${request.path}";
}
@SuppressWarnings("unchecked")
Map<String, String> tags = (Map<String, String>) request.get("tags");
String overrideId = ReservedTags.extractOverrideApiId(tags);
String apiId = overrideId != null ? overrideId : shortId(10);
if (apiStore.get(apiKey(region, apiId)).isPresent()) {
throw new AwsException("ConflictException",
"API with id '" + apiId + "' already exists", 409);
}
Api api = new Api();
api.setApiId(apiId);
api.setName(name);
api.setProtocolType(protocolType);
api.setCreatedDate(System.currentTimeMillis());
api.setRouteSelectionExpression(routeSelectionExpression);
api.setDescription(description);
api.setApiKeySelectionExpression(apiKeySelectionExpression);
api.setDisableExecuteApiEndpoint(booleanValue(request.get("disableExecuteApiEndpoint")));
if ("WEBSOCKET".equals(protocolType)) {
api.setApiEndpoint(String.format("wss://%s.execute-api.%s.amazonaws.com", api.getApiId(), region));
} else {
api.setApiEndpoint(String.format("https://%s.execute-api.%s.amazonaws.com", api.getApiId(), region));
}View on GitHub (pinned to 62ff490619)
Solutions
- Delete the existing API first: aws apigatewayv2 delete-api --api-id <id>, then re-run create.
- If the test suite pins ids, add @AfterEach/@AfterAll cleanup (or unique ids per run) so re-runs start from a clean state.
- Only pass the reserved override tag on the initial creation; drop it on re-creation flows.
- If persistent storage is enabled and the id is stale, clean the persistence store or use a different override id.
Example fix
# before (re-run without cleanup) aws apigatewayv2 create-api --name t --protocol-type HTTP \ --tags floci/override-api-id=demo12345 # after test -z "$(aws apigatewayv2 get-apis --query 'Items[?ApiId==`demo12345`].ApiId' --output text)" || \ aws apigatewayv2 delete-api --api-id demo12345 aws apigatewayv2 create-api --name t --protocol-type HTTP \ --tags floci/override-api-id=demo12345
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check the pinned id before creating
String overrideId = tags.get("floci/override-api-id"); // or your reserved tag key
if (overrideId != null) {
try {
v2Client.getApi(GetApiRequest.builder().apiId(overrideId).build());
v2Client.deleteApi(DeleteApiRequest.builder().apiId(overrideId).build()); // or reuse it
} catch (NotFoundException ok) { /* free to create */ }
}
v2Client.createApi(...); Try / catch
catch (AwsException e) {
if ("ConflictException".equals(e.getCode())) {
// pinned id already live: delete-then-recreate, or skip creation entirely
v2Client.deleteApi(DeleteApiRequest.builder().apiId(pinnedId).build());
return v2Client.createApi(req); // single bounded retry
}
throw e;
} Prevention
- Make test fixtures delete pinned-id APIs in teardown (@AfterEach).
- Only pass the override tag on first creation; store the ApiId for later runs.
- With persistent storage, reset or clean state between CI runs that pin ids.
When it happens
Trigger: Creating an API twice with the same reserved override-id tag (e.g. in a test fixture or a re-run of a setup script that does not delete the prior API). Restoring or importing an API with a fixed id while one with that id still exists. Any createApi call whose tags carry the reserved override id of a live API.
Common situations: Integration-test suites that pin API ids via reserved tags for deterministic ARNs and run twice without cleanup. CI retries that re-apply the same creation template. Emulator state persisted to disk so previously created APIs survive restarts and collide with the next pinned create.
Related errors
- BadRequestException
- ResourceInUseException
- ConflictException
- InstanceRefreshInProgress
- NameAlreadyExistsException
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/3357fd7cc85c3741.
Report an issue: GitHub.