floci-io/floci · error · AwsException

ActionTypeAlreadyExistsException

ActionTypeAlreadyExistsException

Error message

Action type already exists

What it means

Thrown by createCustomActionType (CodePipelineService.java:481) when a custom action type with the same category+provider+version id already exists in the account store. The id string is the composite key, so any collision on those three fields — even with different settings — triggers ActionTypeAlreadyExistsException.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/codepipeline/CodePipelineService.java:481

        putExecution(rollback);
        return started;
    }

    private ObjectNode overrideStageCondition(JsonNode request, String region, String account) {
        requireExecution(account, region, text(request, "pipelineName"), text(request, "pipelineExecutionId"));
        return mapper.createObjectNode();
    }

    private ObjectNode listRuleExecutions(JsonNode request, String region, String account) {
        requirePipeline(account, region, text(request, "pipelineName"));
        return emptyPage("ruleExecutionDetails");
    }

    private ObjectNode createCustomActionType(JsonNode request, String region, String account) {
        String id = actionTypeId(request.path("category").asText(), "Custom",
                request.path("provider").asText(), request.path("version").asText());
        if (itemStore.getForAccount(account, itemKey(region, "action", id)).isPresent()) {
            throw new AwsException("ActionTypeAlreadyExistsException", "Action type already exists", 400);
        }
        ObjectNode actionType = mapper.createObjectNode();
        actionType.setAll((ObjectNode) request.deepCopy());
        actionType.putObject("id")
                .put("category", request.path("category").asText())
                .put("owner", "Custom")
                .put("provider", request.path("provider").asText())
                .put("version", request.path("version").asText());
        storeItem(account, region, "action", id, "Active", actionType);
        return mapper.createObjectNode().set("actionType", actionType);
    }

    private ObjectNode updateActionType(JsonNode request, String region, String account) {
        JsonNode identifier = request.path("actionType");
        String id = actionTypeId(identifier.path("category").asText(), identifier.path("owner").asText(),
                identifier.path("provider").asText(), identifier.path("version").asText());
        CodePipelineStoredItem existing = requireItem(account, region, "action", id, "ActionTypeNotFoundException");
        existing.setData(request.deepCopy());

View on GitHub (pinned to 62ff490619)

Solutions

  1. ListActionTypes first, filter owner=Custom, and skip creation when the id already exists.
  2. Delete the stale custom action type before re-registering, if the semantics changed.
  3. Make registration scripts idempotent (check-then-create) and use unique provider names per team.

Example fix

// before
client.create_custom_action_type(category='Build', provider='Jenkins', version='1', ...)

// after
existing = [a for a in client.list_action_types().get('actionTypes', [])
             if a['id']['owner'] == 'Custom']
have = {(a['id']['category'], a['id']['provider'], a['id']['version']) for a in existing}
if ('Build', 'Jenkins', '1') not in have:
    client.create_custom_action_type(category='Build', provider='Jenkins', version='1', ...)
Defensive patterns

Strategy: validation

Validate before calling

existing = {(a['id']['category'], a['id']['provider'], a['id']['version'])
           for a in client.list_action_types().get('actionTypes', [])
           if a['id']['owner'] == 'Custom'}
if ('Build', 'MyProvider', '1') not in existing:
    client.create_custom_action_type(category='Build', provider='MyProvider',
                                     version='1', inputArtifactDetails=..., outputArtifactDetails=...)

Try / catch

try:
    client.create_custom_action_type(...)
except ClientError as e:
    if e.response['Error']['Code'] == 'ActionTypeAlreadyExistsException':
        logger.info('custom action already registered; skipping')
    else:
        raise

Prevention

When it happens

Trigger: CreateCustomActionType(category=X, provider=P, version=V) called twice; re-running bootstrap scripts that register the same custom action; two teams independently registering provider 'Jenkins' version '1'.

Common situations: Non-idempotent setup scripts run per CI job; environment rebuilds without cleanup; provider naming collisions across projects sharing one account.

Related errors


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