{"record":{"id":"2a323644dd9df8fe","repo":"apache/cassandra","slug":"ttl-for-snapshot-must-be-at-least-d-seconds","errorCode":null,"errorMessage":"ttl for snapshot must be at least %d seconds","messagePattern":"ttl for snapshot must be at least (.+?) seconds","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/snapshot/SnapshotOptions.java","lineNumber":269,"sourceCode":"            // snapshot names via version build metadata (e.g. an upgrade snapshot \"<millis>-upgrade-5.0.4+build-...\").\n            // The remaining S3-safe characters (! * ' ( )) are intentionally excluded as they are\n            // shell-significant and error-prone in paths, and the path separator '/' is excluded too,\n            // which is what blocks traversal attempts such as \"../../mysnapshot\"\n            if (!SAFE_SNAPSHOT_NAME.matcher(resolvedSnapshotName).matches())\n            {\n                throw new IllegalArgumentException(\"Snapshot name contains illegal characters: \" + resolvedSnapshotName + \". \" +\n                                                   \"Allowed characters must match the pattern: \" + SAFE_SNAPSHOT_NAME.pattern() +\n                                                   \" with a maximum of length of \" + FILENAME_LENGTH + \" characters.\");\n            }\n        }\n\n        private void validateTTL(boolean ephemeral, DurationSpec.IntSecondsBound ttl)\n        {\n            if (ttl != null)\n            {\n                int minAllowedTtlSecs = CassandraRelevantProperties.SNAPSHOT_MIN_ALLOWED_TTL_SECONDS.getInt();\n                if (ttl.toSeconds() < minAllowedTtlSecs)\n                    throw new IllegalArgumentException(format(\"ttl for snapshot must be at least %d seconds\", minAllowedTtlSecs));\n            }\n\n            if (ephemeral && ttl != null)\n                throw new IllegalStateException(format(\"can not take ephemeral snapshot (%s) while ttl is specified too\", tag));\n        }\n    }\n\n    @Override\n    public String toString()\n    {\n        return \"CreateSnapshotOptions{\" +\n               \"type=\" + type +\n               \", tag='\" + tag + '\\'' +\n               \", ttl=\" + ttl +\n               \", creationTime=\" + creationTime +\n               \", skipFlush=\" + skipFlush +\n               \", ephemeral=\" + ephemeral +\n               \", entities=\" + Arrays.toString(entities) +","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/snapshot/SnapshotOptions.java#L251-L287","documentation":"validateTTL enforces a minimum TTL for snapshots, read from the SNAPSHOT_MIN_ALLOWED_TTL_SECONDS property. A snapshot created with a ttl smaller than this minimum is rejected with IllegalArgumentException, because very short TTLs would make TTL-based snapshot cleanup churn effectively useless. The ttl parameter is only checked when it is non-null (i.e. the caller requested a TTL-bound snapshot).","triggerScenarios":"nodetool snapshot --ttl <small-duration> (e.g. --ttl 5s) or SnapshotOptions.builder().withTTL(DurationSpec.IntSecondsBound of a value below cassandra.snapshot.min_allowed_ttl_seconds, default 60).","commonSituations":"Users passing a small ttl expecting a short-lived snapshot for quick testing; scripts with unit confusion (seconds vs milliseconds); clusters where the min TTL property was raised but tooling still uses old values.","solutions":["Increase the --ttl value to at least the configured minimum (default 60 seconds).","Lower cassandra.snapshot.min_allowed_ttl_seconds in cassandra.yaml / -Dcassandra.snapshot.min_allowed_ttl_seconds if genuinely shorter TTLs are needed.","Omit ttl entirely if the snapshot should be permanent."],"exampleFix":"// before\nnodetool snapshot -t daily --ttl 10s\n// after\nnodetool snapshot -t daily --ttl 60s","handlingStrategy":"validation","validationCode":"int minTtl = Integer.getInteger(\"cassandra.snapshot.min_allowed_ttl_seconds\", 60);\nif (ttlSeconds < minTtl) ttlSeconds = minTtl; // clamp before invoking","typeGuard":null,"tryCatchPattern":"try { snapshotWithTtl(ttl); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"at least\")) retryWithTtl(minAllowedTtl); else throw e; }","preventionTips":["Read cassandra.snapshot.min_allowed_ttl_seconds from config before choosing ttl","Use seconds consistently to avoid unit confusion","Omit ttl for permanent snapshots"],"tags":["snapshot","ttl","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}