apache/cassandra · error · IllegalArgumentException

ttl for snapshot must be at least

Error message

ttl for snapshot must be at least %d seconds

What it means

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).

Solutions

  1. Increase the --ttl value to at least the configured minimum (default 60 seconds).
  2. Lower cassandra.snapshot.min_allowed_ttl_seconds in cassandra.yaml / -Dcassandra.snapshot.min_allowed_ttl_seconds if genuinely shorter TTLs are needed.
  3. Omit ttl entirely if the snapshot should be permanent.

Example fix

// before
nodetool snapshot -t daily --ttl 10s
// after
nodetool snapshot -t daily --ttl 60s
Defensive patterns

Strategy: validation

Validate before calling

int minTtl = Integer.getInteger("cassandra.snapshot.min_allowed_ttl_seconds", 60);
if (ttlSeconds < minTtl) ttlSeconds = minTtl; // clamp before invoking

Try / catch

try { snapshotWithTtl(ttl); } catch (IllegalArgumentException e) { if (e.getMessage().contains("at least")) retryWithTtl(minAllowedTtl); else throw e; }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/2a323644dd9df8fe. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/service/snapshot/SnapshotOptions.java:269

            // snapshot names via version build metadata (e.g. an upgrade snapshot "<millis>-upgrade-5.0.4+build-...").
            // The remaining S3-safe characters (! * ' ( )) are intentionally excluded as they are
            // shell-significant and error-prone in paths, and the path separator '/' is excluded too,
            // which is what blocks traversal attempts such as "../../mysnapshot"
            if (!SAFE_SNAPSHOT_NAME.matcher(resolvedSnapshotName).matches())
            {
                throw new IllegalArgumentException("Snapshot name contains illegal characters: " + resolvedSnapshotName + ". " +
                                                   "Allowed characters must match the pattern: " + SAFE_SNAPSHOT_NAME.pattern() +
                                                   " with a maximum of length of " + FILENAME_LENGTH + " characters.");
            }
        }

        private void validateTTL(boolean ephemeral, DurationSpec.IntSecondsBound ttl)
        {
            if (ttl != null)
            {
                int minAllowedTtlSecs = CassandraRelevantProperties.SNAPSHOT_MIN_ALLOWED_TTL_SECONDS.getInt();
                if (ttl.toSeconds() < minAllowedTtlSecs)
                    throw new IllegalArgumentException(format("ttl for snapshot must be at least %d seconds", minAllowedTtlSecs));
            }

            if (ephemeral && ttl != null)
                throw new IllegalStateException(format("can not take ephemeral snapshot (%s) while ttl is specified too", tag));
        }
    }

    @Override
    public String toString()
    {
        return "CreateSnapshotOptions{" +
               "type=" + type +
               ", tag='" + tag + '\'' +
               ", ttl=" + ttl +
               ", creationTime=" + creationTime +
               ", skipFlush=" + skipFlush +
               ", ephemeral=" + ephemeral +
               ", entities=" + Arrays.toString(entities) +

View on GitHub (pinned to 88fd0f6a0e)