{"record":{"id":"bfee4562271dc2bd","repo":"apache/cassandra","slug":"snapshot-name-is-reserved","errorCode":null,"errorMessage":"Snapshot name '' is reserved","messagePattern":"Snapshot name '' is reserved","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/snapshot/SnapshotOptions.java","lineNumber":225,"sourceCode":"            if (rateLimiter == null)\n                rateLimiter = DatabaseDescriptor.getSnapshotRateLimiter();\n\n            return new SnapshotOptions(this);\n        }\n\n        private void validateTag(String tag)\n        {\n            if (tag == null || tag.isEmpty())\n                throw new IllegalArgumentException(\"You must supply a snapshot name.\");\n\n            if (tag.contains(File.pathSeparator()))\n            {\n                throw new IllegalArgumentException(\"Snapshot name cannot contain \" + File.pathSeparator());\n            }\n\n            if (tag.equals(\".\") || tag.equals(\"..\"))\n            {\n                throw new IllegalArgumentException(\"Snapshot name '\" + tag + \"' is reserved\");\n            }\n\n            if (!CassandraRelevantProperties.SNAPSHOT_NAME_VALIDATION.getBoolean())\n                return;\n\n            // Pre-generate snapshot name for the sake of the validation.\n            // getSnapshotName logic does not return raw \"tag\" as snapshot name every time,\n            // it e.g. prepends timestamp and type for system snapshots, and we need to validate it as a whole.\n            // If, for example, tag would be less than max allowed FILENAME_LENGTH,\n            // we might in fact produce a snapshot name longer than FILENAME_LENGTH if we prepended a timestamp to it.\n            String resolvedSnapshotName = SnapshotOptions.getSnapshotName(type, tag, now());\n\n            // the length of valid snapshot name has to be less than or equal to FILENAME_LEGTH - that is 255 -\n            // we are following the max length as it is in SchemaConstants for table name.\n            if (resolvedSnapshotName.length() > SchemaConstants.FILENAME_LENGTH)\n            {\n                throw new IllegalArgumentException(format(\"Snapshot name must not be more than %d characters long for \" +\n                                                          \"resolved snapshot name (got %d characters for \\\"%s\\\")\",","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/snapshot/SnapshotOptions.java#L207-L243","documentation":"IllegalArgumentException from SnapshotOptions.validateTag: the tag reaching validation is empty, producing the literal reserved-name message. validateTag is a guard over user-supplied snapshot names; it fires when callers bypass the earlier 'must supply a name' check with a value that resolves to '' (or is path-traversal-reserved like '.'/'..'), which Cassandra reserves and never allows as a snapshot directory name.","triggerScenarios":"Passing tag '.' or '..' (literally or produced by path-normalization logic that strips a directory component).","commonSituations":"Scripts that join a directory path and use its last element as the tag, ending up with '..'; hand-edited configs containing dot segments.","solutions":["Use a descriptive, non-reserved tag (e.g. 'snap-2024-01-01').","Normalize the path first and pick a real filename component, not '.' or '..'.","Add a guard in scripts: if [ \"$tag\" = \".\" ] || [ \"$tag\" = \"..\" ]; then abort; fi.","Quote variables in shell scripts to avoid accidental dot-segment expansion."],"exampleFix":"// before\nString tag = new File(dir).getName(); // dir=\"..\" -> tag=\"..\"\ntakeSnapshot(tag, keyspace);\n// after\nString tag = \"snap-\" + new File(dir).getCanonicalFile().getName();\ntakeSnapshot(tag, keyspace);","handlingStrategy":"validation","validationCode":"boolean isReserved(String tag) { return \".\".equals(tag) || \"..\".equals(tag); }","typeGuard":null,"tryCatchPattern":"try { takeSnapshot(tag, ks); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"is reserved\")) { tag = \"snap-\" + System.currentTimeMillis(); retry(); } }","preventionTips":["Never derive tags from raw path segments","Normalize paths before extracting tag names","Quote shell variables to avoid dot-segment surprises"],"tags":["snapshot","validation","illegalargument","reserved-name"],"backgroundTag":"invalid-argument-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}