{"record":{"id":"688270ab7901db53","repo":"apache/cassandra","slug":"cannot-snapshot-until-bootstrap-completes","errorCode":null,"errorMessage":"Cannot snapshot until bootstrap completes","messagePattern":"Cannot snapshot until bootstrap completes","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/snapshot/TakeSnapshotTask.java","lineNumber":80,"sourceCode":"    private String snapshotName;\n    private final Map<ColumnFamilyStore, TableSnapshot> snapshotsToCreate = new HashMap<>();\n\n    public TakeSnapshotTask(SnapshotManager manager, SnapshotOptions options)\n    {\n        super(options);\n        this.manager = manager;\n    }\n\n    @Override\n    public SnapshotTaskType getTaskType()\n    {\n        return SnapshotTaskType.SNAPSHOT;\n    }\n\n    public Map<ColumnFamilyStore, TableSnapshot> getSnapshotsToCreate()\n    {\n        if (StorageService.instance.operationMode() == StorageService.Mode.JOINING)\n            throw new RuntimeException(\"Cannot snapshot until bootstrap completes\");\n\n        // This is not in builder's build method on purpose in order to postpone the timestamp for as long as possible\n        // until the actual snapshot is taken. If we constructed a task and have not done anything with it for 5 minutes\n        // then by the time a snapshot would be taken the creation time would be quite off\n        Instant creationTimeInOptions = options.creationTime;\n        if (creationTimeInOptions == null)\n            creationTime = Instant.ofEpochMilli(Clock.Global.currentTimeMillis());\n        else\n            creationTime = options.creationTime;\n\n        snapshotName = SnapshotOptions.getSnapshotName(options.type, options.tag, creationTime);\n\n        Set<ColumnFamilyStore> entitiesForSnapshot = options.cfs == null ? parseEntitiesForSnapshot(options.entities) : Set.of(options.cfs);\n\n        for (ColumnFamilyStore cfs : entitiesForSnapshot)\n        {\n            Set<File> snapshotDirs = cfs.getDirectories().getSnapshotDirsWithoutCreation(snapshotName);\n","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/snapshot/TakeSnapshotTask.java#L62-L98","documentation":"TakeSnapshotTask.getSnapshotsToCreate refuses to take snapshots while the node's StorageService operation mode is JOINING, throwing RuntimeException. During bootstrap the node is still streaming token ranges and its data is incomplete/in-flux, so a snapshot would not represent consistent, fully-owned data. This check was deliberately placed in getSnapshotsToCreate (not the builder) so the mode is evaluated as late as possible, right before the snapshot executes.","triggerScenarios":"Running nodetool snapshot while the node is still bootstrapping into the cluster (operationMode() == JOINING); automation that fires snapshots immediately after node start before bootstrap completes.","commonSituations":"Backup cron jobs that start when the node process is up but before join completes; large bootstraps (big data streaming) overlapping scheduled snapshot windows; monitoring scripts snapshotting new nodes too early.","solutions":["Wait until the node finishes joining (nodetool status shows UN, or operationMode is NORMAL) before snapshotting.","Add automation guards that check nodetool info / StorageService mode and defer the snapshot until bootstrap completes.","Retry the snapshot after bootstrap; nothing is corrupted by the earlier failure."],"exampleFix":"// before\nnodetool snapshot -t backup   # immediately after restart/join\n// after\nuntil nodetool info | grep -q 'Mode: NORMAL'; do sleep 30; done\nnodetool snapshot -t backup","handlingStrategy":"retry","validationCode":"if (StorageService.instance.operationMode() == StorageService.Mode.JOINING)\n    throw new IllegalStateException(\"Node still joining; postpone snapshot\");","typeGuard":null,"tryCatchPattern":"try { takeSnapshot(); } catch (RuntimeException e) { if (e.getMessage().contains(\"bootstrap\")) { waitUntilNormal(); takeSnapshot(); } else throw e; }","preventionTips":["Gate backup jobs on nodetool info showing Mode: NORMAL","Wait for all tokens to be UN in nodetool status after bootstrap","Avoid snapshots immediately after node start/restart"],"tags":["snapshot","bootstrap","node-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}