floci-io/floci · error · AwsException

PreconditionFailed

PreconditionFailed

Error message

The precondition in one or more request-header fields evaluated to false.

What it means

CloudFrontService.updatePublicKey throws PreconditionFailed (HTTP 412) when the If-Match header does not equal the key's current ETag. Public keys use ETag-based optimistic concurrency identical to the rest of CloudFront; each successful write rotates the UUID ETag and the next write must present the newest one.

Source

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

                    "A public key with this caller reference already exists.",
                    409);
        }
        key.setId(UUID.randomUUID().toString());
        key.setCreatedTime(Instant.now());
        key.setEtag(UUID.randomUUID().toString());
        publicKeyStore.put(key.getId(), key);
        return key;
    }

    public PublicKey getPublicKey(String id) {
        return publicKeyStore.get(id).orElseThrow(() ->
                new AwsException("NoSuchPublicKey", "The specified public key does not exist.", 404));
    }

    public synchronized PublicKey updatePublicKey(String id, String ifMatch, PublicKey updated) {
        PublicKey existing = getPublicKey(id);
        if (!existing.getEtag().equals(ifMatch)) {
            throw new AwsException(
                    "PreconditionFailed",
                    "The precondition in one or more request-header fields evaluated to false.",
                    412);
        }
        validatePublicKey(updated);
        if (!Objects.equals(
                    existing.getCallerReference(),
                    updated.getCallerReference())
                || !Objects.equals(existing.getName(), updated.getName())
                || !Objects.equals(
                    existing.getEncodedKey(), updated.getEncodedKey())) {
            throw new AwsException(
                    "CannotChangeImmutablePublicKeyFields",
                    "The caller reference, name, and encoded public key cannot be changed.",
                    400);
        }
        updated.setId(id);
        updated.setCreatedTime(existing.getCreatedTime());

View on GitHub (pinned to 62ff490619)

Solutions

  1. GetPublicKey immediately before updating and pass its ETag in If-Match.
  2. Carry the ETag returned by each UpdatePublicKey forward to the next mutation.
  3. Catch PreconditionFailed, refetch the ETag, and retry the update once.

Example fix

// before
String etag = createResp.eTag();
updateKey(id, etag, v1);
updateKey(id, etag, v2); // 412

// after
var r1 = updateKey(id, createResp.eTag(), v1);
var r2 = updateKey(id, r1.eTag(), v2);
Defensive patterns

Strategy: retry

Try / catch

for (int i = 0; i < 3; i++) {
    String etag = client.getPublicKey(r -> r.id(id)).eTag();
    try {
        return client.updatePublicKey(r -> r.id(id).ifMatch(etag).publicKeyConfig(cfg));
    } catch (PreconditionFailed e) {
        // etag rotated; refetch and retry
    }
}

Prevention

When it happens

Trigger: UpdatePublicKey with an ETag captured at creation when a prior update already rotated it; omitting If-Match; concurrent updates to the same key id where the loser's ETag is stale.

Common situations: Scripts that update a key's comment twice while caching the create-time ETag; stateful CLI sessions that hold old descriptions after background automation touched the key.

Related errors


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