{"record":{"id":"20b8361e590ee34f","repo":"apache/cassandra","slug":"can-t-move-to-the-undefined-null-token","errorCode":null,"errorMessage":"Can't move to the undefined (null) token.","messagePattern":"Can't move to the undefined \\(null\\) token\\.","errorType":"exception","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java","lineNumber":175,"sourceCode":"    }\n\n    static void abortRemoveNode(String nodeId)\n    {\n        abortHelper(nodeId, MultiStepOperation.Kind.REMOVE, null);\n    }\n\n    /**\n     * move the node to new token or find a new token to boot to according to load\n     *\n     * @param newToken new token to boot to, or if null, find balanced token to boot to\n     */\n    static void move(Token newToken)\n    {\n        if (ClusterMetadataService.instance().isMigrating() || ClusterMetadataService.state() == ClusterMetadataService.State.GOSSIP)\n            throw new IllegalStateException(\"This cluster is migrating to cluster metadata, can't move until that is done.\");\n\n        if (newToken == null)\n            throw new IllegalArgumentException(\"Can't move to the undefined (null) token.\");\n\n        if (ClusterMetadata.current().tokenMap.tokens().contains(newToken))\n            throw new IllegalArgumentException(String.format(\"target token %s is already owned by another node.\", newToken));\n\n        // address of the current node\n        ClusterMetadata metadata = ClusterMetadata.current();\n        NodeId self = metadata.myNodeId();\n        // This doesn't make any sense in a vnodes environment.\n        if (metadata.tokenMap.tokens(self).size() > 1)\n        {\n            logger.error(\"Invalid request to move(Token); This node has more than one token and cannot be moved thusly.\");\n            throw new UnsupportedOperationException(\"This node has more than one token and cannot be moved thusly.\");\n        }\n\n        ClusterMetadataService.instance().commit(new PrepareMove(self,\n                                                                 Collections.singleton(newToken),\n                                                                 ClusterMetadataService.instance().placementProvider(),\n                                                                 true),","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java#L157-L193","documentation":"move(Token) validates its argument and throws IllegalArgumentException when newToken is null, because moving to an undefined token is meaningless - the caller must supply an explicit target token. (Passing no token at all is handled by a different code path that picks a balanced token.)","triggerScenarios":"Invoking the JMX/StorageService move operation with a null or unparsable token argument such that `nodetool move` passes null into SingleNodeSequences.move.","commonSituations":"Scripted tooling calling the JMX MBean with an empty string argument that deserializes to null; a wrapper that lost the token argument; typos in scripts passing an unset variable.","solutions":["Pass an explicit valid token: `nodetool move <token>`.","If you want an automatically balanced token, use the no-token/bootstrap path instead of move(null).","Fix scripts to validate the token argument is non-empty before calling the MBean."],"exampleFix":"// before\nss.move(nullToken);\n// after\nif (newToken == null) throw new IllegalArgumentException(\"token is required\");\nss.move(newToken);","handlingStrategy":"validation","validationCode":"Objects.requireNonNull(newToken, \"target token is required for move\");","typeGuard":"boolean isValidToken(Token t) { return t != null; }","tryCatchPattern":"try { move(token); }\ncatch (IllegalArgumentException e) { if (e.getMessage().contains(\"undefined (null)\")) { /* fix caller arg */ } else throw e; }","preventionTips":["Validate CLI/script arguments non-empty before invoking the move MBean","Use nodetool's parsing path rather than raw JMX with untyped strings","Log the token value at call sites to catch unset variables"],"tags":["cassandra","argument-validation","null-argument","topology"],"backgroundTag":"null-argument","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"}