{"record":{"id":"704799e3f3bb65b5","repo":"floci-io/floci","slug":"invalidifmatchversion-704799","errorCode":"InvalidIfMatchVersion","errorMessage":"The If-Match version is missing or not valid for the resource.","messagePattern":"The If-Match version is missing or not valid for the resource\\.","errorType":"error_code","errorClass":"AwsException","httpStatus":400,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/services/cloudfront/CloudFrontService.java","lineNumber":142,"sourceCode":"        dist.setLastModifiedTime(Instant.now());\n        dist.setEtag(UUID.randomUUID().toString());\n        if (tags != null && !tags.isEmpty()) {\n            dist.setTags(tags);\n            tagStore.put(\"distribution/\" + id, tags);\n        }\n        distStore.put(id, dist);\n        return dist;\n    }\n\n    public Distribution getDistribution(String id) {\n        return distStore.get(id).orElseThrow(() ->\n                new AwsException(\"NoSuchDistribution\", \"The specified distribution does not exist.\", 404));\n    }\n\n    public synchronized Distribution updateDistribution(String id, String ifMatch, Distribution updated) {\n        Distribution existing = getDistribution(id);\n        if (!existing.getEtag().equals(ifMatch)) {\n            throw new AwsException(\"InvalidIfMatchVersion\",\n                    \"The If-Match version is missing or not valid for the resource.\", 400);\n        }\n        ensureAliasesAvailable(updated.getConfig(), id);\n        validateTrustedKeyGroups(updated.getConfig());\n        updated.setId(id);\n        updated.setArn(existing.getArn());\n        updated.setDomainName(existing.getDomainName());\n        updated.setStatus(\"Deployed\");\n        updated.setLastModifiedTime(Instant.now());\n        updated.setEtag(UUID.randomUUID().toString());\n        updated.setTags(existing.getTags());\n        distStore.put(id, updated);\n        return updated;\n    }\n\n    public synchronized void deleteDistribution(String id, String ifMatch) {\n        Distribution existing = getDistribution(id);\n        if (!existing.getEtag().equals(ifMatch)) {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/services/cloudfront/CloudFrontService.java#L124-L160","documentation":"Floci's CloudFront service layer compares the caller's If-Match value against the distribution's stored ETag on UpdateDistribution. Unlike the controller-side checks (missing header), this fires when a header WAS sent but does not equal the current ETag — the emulator rotates the ETag (UUID) on every update, so any stale value fails.","triggerScenarios":"PUT /distribution/{Id}/config with an If-Match value that differs from the stored ETag: reused ETag from an earlier update, ETag of a different distribution, or a value like '*' that the emulator does not special-case.","commonSituations":"Caching the ETag across multiple updates instead of re-reading after each one; concurrent writers where one update invalidates the other's ETag; assuming '*' is accepted as any-version.","solutions":["Re-GET the distribution config immediately before each PUT and use the fresh ETag","On this error, treat it as a conflict: reload the config, reapply your changes, and retry once","Never reuse an ETag after a successful update — it is regenerated per write"],"exampleFix":"// before: etag cached once and reused\ncf.updateDistribution(req -> req.id(id).ifMatch(cachedEtag).distributionConfig(cfg));\n\n// after: refresh each time\nString etag = cf.getDistributionConfig(r -> r.id(id)).eTag();\ncf.updateDistribution(req -> req.id(id).ifMatch(etag).distributionConfig(cfg));","handlingStrategy":"retry","validationCode":"// precondition: ETag must be the one from the most recent read\nString current = cf.getDistributionConfig(r -> r.id(id)).eTag();\nif (!current.equals(ifMatchValue)) {\n    // stale: reload config and merge your changes before updating\n}","typeGuard":null,"tryCatchPattern":"for (int attempt = 0; attempt < 2; attempt++) {\n    try {\n        String etag = cf.getDistributionConfig(r -> r.id(id)).eTag();\n        cf.updateDistribution(req -> req.id(id).ifMatch(etag).distributionConfig(cfg));\n        break;\n    } catch (CloudFrontException e) {\n        if (\"InvalidIfMatchVersion\".equals(e.awsErrorDetails().errorCode()) && attempt == 0) {\n            continue; // ETag raced; re-read and retry once\n        }\n        throw e;\n    }\n}","preventionTips":["Scope ETag lifetime to a single read-modify-write cycle","Expect the emulator (like AWS) to mint a new ETag per update; never cache across writes"],"tags":["cloudfront","etag","optimistic-concurrency","distribution","conflict"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}