apache/cassandra · error · java.lang.IllegalArgumentException

Can't move to the undefined (null) token.

Error message

Can't move to the undefined (null) token.

What it means

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

Source

Thrown at src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java:175

    }

    static void abortRemoveNode(String nodeId)
    {
        abortHelper(nodeId, MultiStepOperation.Kind.REMOVE, null);
    }

    /**
     * move the node to new token or find a new token to boot to according to load
     *
     * @param newToken new token to boot to, or if null, find balanced token to boot to
     */
    static void move(Token newToken)
    {
        if (ClusterMetadataService.instance().isMigrating() || ClusterMetadataService.state() == ClusterMetadataService.State.GOSSIP)
            throw new IllegalStateException("This cluster is migrating to cluster metadata, can't move until that is done.");

        if (newToken == null)
            throw new IllegalArgumentException("Can't move to the undefined (null) token.");

        if (ClusterMetadata.current().tokenMap.tokens().contains(newToken))
            throw new IllegalArgumentException(String.format("target token %s is already owned by another node.", newToken));

        // address of the current node
        ClusterMetadata metadata = ClusterMetadata.current();
        NodeId self = metadata.myNodeId();
        // This doesn't make any sense in a vnodes environment.
        if (metadata.tokenMap.tokens(self).size() > 1)
        {
            logger.error("Invalid request to move(Token); This node has more than one token and cannot be moved thusly.");
            throw new UnsupportedOperationException("This node has more than one token and cannot be moved thusly.");
        }

        ClusterMetadataService.instance().commit(new PrepareMove(self,
                                                                 Collections.singleton(newToken),
                                                                 ClusterMetadataService.instance().placementProvider(),
                                                                 true),

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Pass an explicit valid token: `nodetool move <token>`.
  2. If you want an automatically balanced token, use the no-token/bootstrap path instead of move(null).
  3. Fix scripts to validate the token argument is non-empty before calling the MBean.

Example fix

// before
ss.move(nullToken);
// after
if (newToken == null) throw new IllegalArgumentException("token is required");
ss.move(newToken);
Defensive patterns

Strategy: validation

Validate before calling

Objects.requireNonNull(newToken, "target token is required for move");

Type guard

boolean isValidToken(Token t) { return t != null; }

Try / catch

try { move(token); }
catch (IllegalArgumentException e) { if (e.getMessage().contains("undefined (null)")) { /* fix caller arg */ } else throw e; }

Prevention

When it happens

Trigger: Invoking the JMX/StorageService move operation with a null or unparsable token argument such that `nodetool move` passes null into SingleNodeSequences.move.

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

Related errors


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