floci-io/floci · error · AwsException

DistributionNotDisabled

DistributionNotDisabled

Error message

The distribution you are trying to delete has not been disabled.

What it means

Floci's CloudFront service layer refuses to delete a distribution whose config still has Enabled=true, returning DistributionNotDisabled (HTTP 409). AWS requires a distribution to be fully disabled before deletion, and the emulator enforces the same state machine after the ETag check passes.

Source

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

        updated.setId(id);
        updated.setArn(existing.getArn());
        updated.setDomainName(existing.getDomainName());
        updated.setStatus("Deployed");
        updated.setLastModifiedTime(Instant.now());
        updated.setEtag(UUID.randomUUID().toString());
        updated.setTags(existing.getTags());
        distStore.put(id, updated);
        return updated;
    }

    public synchronized void deleteDistribution(String id, String ifMatch) {
        Distribution existing = getDistribution(id);
        if (!existing.getEtag().equals(ifMatch)) {
            throw new AwsException("InvalidIfMatchVersion",
                    "The If-Match version is missing or not valid for the resource.", 400);
        }
        if (existing.getConfig() != null && existing.getConfig().isEnabled()) {
            throw new AwsException("DistributionNotDisabled",
                    "The distribution you are trying to delete has not been disabled.", 409);
        }
        distStore.delete(id);
        invalidationStore.delete(id);
        tagStore.delete("distribution/" + id);
    }

    /**
     * Removes a distribution and its associated invalidations/tags without the disable/If-Match guards
     * enforced by {@link #deleteDistribution(String, String)}. Used by CloudFormation stack deletion,
     * which owns the resource lifecycle at the stack level.
     */
    public synchronized void removeDistribution(String id) {
        distStore.delete(id);
        invalidationStore.delete(id);
        tagStore.delete("distribution/" + id);
    }

View on GitHub (pinned to 62ff490619)

Solutions

  1. First update the distribution with Enabled=false (using the current ETag as If-Match)
  2. Then re-GET the config for the new ETag and issue the delete with it
  3. Model the full sequence: disable -> (optionally wait for status) -> delete

Example fix

// before
cf.deleteDistribution(req -> req.id(id).ifMatch(etag));

// after: disable first, then delete with refreshed etag
DistributionConfig disabled = cfg.toBuilder().enabled(false).build();
cf.updateDistribution(req -> req.id(id).ifMatch(etag).distributionConfig(disabled));
String freshEtag = cf.getDistributionConfig(r -> r.id(id)).eTag();
cf.deleteDistribution(req -> req.id(id).ifMatch(freshEtag));
Defensive patterns

Strategy: validation

Validate before calling

GetDistributionConfigResponse cur = cf.getDistributionConfig(r -> r.id(id));
if (Boolean.TRUE.equals(cur.distributionConfig().enabled())) {
    // must disable first: update with enabled=false using cur.eTag()
}

Try / catch

try {
    cf.deleteDistribution(req -> req.id(id).ifMatch(etag));
} catch (CloudFrontException e) {
    if ("DistributionNotDisabled".equals(e.awsErrorDetails().errorCode())) {
        DistributionConfig off = cfg.toBuilder().enabled(false).build();
        cf.updateDistribution(req -> req.id(id).ifMatch(etag).distributionConfig(off));
        String fresh = cf.getDistributionConfig(r -> r.id(id)).eTag();
        cf.deleteDistribution(req -> req.id(id).ifMatch(fresh));
    } else { throw e; }
}

Prevention

When it happens

Trigger: DELETE /distribution/{Id} with a valid If-Match while the distribution's DistributionConfig has Enabled=true.

Common situations: Teardown scripts that create, test, and delete in one run without a disable step; forgetting that disable is itself an update (which also rotates the ETag); assuming the emulator skips lifecycle guards.

Related errors


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