floci-io/floci · error · AwsException

StreamingDistributionNotDisabled

StreamingDistributionNotDisabled

Error message

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

What it means

A streaming distribution must be disabled before it can be deleted; Floci checks existing.isEnabled() in deleteStreamingDistribution and throws StreamingDistributionNotDisabled (HTTP 409 Conflict) when the distribution is still enabled. The ETag check runs first, so a valid If-Match is still required. AWS behaves identically, and disabling is itself an update that requires the current ETag and takes the distribution out of deployment.

Source

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

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

    public synchronized void deleteStreamingDistribution(String id, String ifMatch) {
        StreamingDistribution existing = getStreamingDistribution(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.isEnabled()) {
            throw new AwsException("StreamingDistributionNotDisabled",
                    "The streaming distribution you are trying to delete has not been disabled.", 409);
        }
        streamingDistStore.delete(id);
    }

    // ── Field-Level Encryption Configs ────────────────────────────────────────

    public synchronized FieldLevelEncryptionConfig createFieldLevelEncryptionConfig(
            FieldLevelEncryptionConfig cfg) {
        cfg.setId(UUID.randomUUID().toString());
        cfg.setLastModifiedTime(Instant.now());
        cfg.setEtag(UUID.randomUUID().toString());
        fleConfigStore.put(cfg.getId(), cfg);
        return cfg;
    }

    public FieldLevelEncryptionConfig getFieldLevelEncryptionConfig(String id) {
        return fleConfigStore.get(id).orElseThrow(() ->

View on GitHub (pinned to 62ff490619)

Solutions

  1. First disable: GetStreamingDistributionConfig, then UpdateStreamingDistribution with Enabled=false and the current ETag
  2. Then GetStreamingDistribution again for the new ETag and call DeleteStreamingDistribution with it
  3. In cleanup code, catch StreamingDistributionNotDisabled and run the disable-then-delete sequence

Example fix

// before
cloudFrontClient.deleteStreamingDistribution(r -> r.id(distId).ifMatch(etag));

// after
GetStreamingDistributionConfigResponse cfg =
    cloudFrontClient.getStreamingDistributionConfig(r -> r.id(distId));
cloudFrontClient.updateStreamingDistribution(r -> r
    .id(distId)
    .ifMatch(cfg.eTag())
    .streamingDistributionConfig(cfg.streamingDistributionConfig().toBuilder()
        .enabled(false)
        .build()));
String newEtag = cloudFrontClient.getStreamingDistributionConfig(r -> r.id(distId)).eTag();
cloudFrontClient.deleteStreamingDistribution(r -> r.id(distId).ifMatch(newEtag));
Defensive patterns

Strategy: try-catch

Validate before calling

GetStreamingDistributionResponse dist = cloudFrontClient.getStreamingDistribution(r -> r.id(distId));
if (dist.streamingDistribution().enabled()) {
    // must disable before delete
}

Try / catch

try {
    cloudFrontClient.deleteStreamingDistribution(r -> r.id(distId).ifMatch(etag));
} catch (CloudFrontException e) {
    if ("StreamingDistributionNotDisabled".equals(e.awsErrorDetails().errorCode())) {
        var cfg = cloudFrontClient.getStreamingDistributionConfig(r -> r.id(distId));
        cloudFrontClient.updateStreamingDistribution(r -> r.id(distId)
            .ifMatch(cfg.eTag())
            .streamingDistributionConfig(cfg.streamingDistributionConfig().toBuilder()
                .enabled(false).build()));
        String newEtag = cloudFrontClient.getStreamingDistributionConfig(r -> r.id(distId)).eTag();
        cloudFrontClient.deleteStreamingDistribution(r -> r.id(distId).ifMatch(newEtag));
    } else { throw e; }
}

Prevention

When it happens

Trigger: DeleteStreamingDistribution on a distribution whose StreamingDistributionConfig.Enabled is true. Typical in teardown scripts that skip the disable step, or that disable and delete so fast the disable update was never actually sent.

Common situations: CI/CD teardown pipelines deleting distributions in one step; test cleanup between runs; forgetting that disable is a separate UpdateStreamingDistribution call with its own If-Match.

Related errors


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