Skip to content

Missing commons-lang3 runtime dependency causes NoClassDefFoundError in DefaultLoadBalancingPolicy (4.19.0.5) #801

@arumus

Description

@arumus

Bug Report

Version

com.scylladb:java-driver-core:4.19.0.5

Description

Starting with version 4.19.0.5, DefaultLoadBalancingPolicy.moveReplicasToFront() uses org.apache.commons.lang3.tuple.Pair, but commons-lang3 is not declared as a dependency in core/pom.xml (or the parent POM). This causes a NoClassDefFoundError / ClassNotFoundException at runtime when the load balancing policy attempts to build a query plan for regular (non-LWT) requests on multi-node clusters.

Root Cause

PR #784 ("Fix: LWT routing enhancements to utilize LBP"), merged on 2026-01-27, refactored DefaultLoadBalancingPolicy to return Pair<Integer, Integer> from the moveReplicasToFront method:

import org.apache.commons.lang3.tuple.Pair;
// ...
private Pair<Integer, Integer> moveReplicasToFront(Object[] currentNodes, List<Node> allReplicas) {
    // ...
    return Pair.of(replicaCount, localRackReplicaCount);
}

However, the corresponding org.apache.commons:commons-lang3 dependency was never added to core/pom.xml or the parent pom.xml.

Why this is not caught during testing

The failure only occurs in multi-node production clusters where DefaultLoadBalancingPolicy.newQueryPlanRegular() is invoked and replicas are non-empty. Single-node integration tests don't exercise the moveReplicasToFront code path because there are no replicas to move, so the Pair class is never loaded.

Stack Trace

java.lang.NoClassDefFoundError: org/apache/commons/lang3/tuple/Pair
    at com.datastax.oss.driver.internal.core.loadbalancing.DefaultLoadBalancingPolicy.moveReplicasToFront(DefaultLoadBalancingPolicy.java:268)
    at com.datastax.oss.driver.internal.core.loadbalancing.DefaultLoadBalancingPolicy.newQueryPlanRegular(DefaultLoadBalancingPolicy.java:205)
    at com.datastax.oss.driver.internal.core.loadbalancing.DefaultLoadBalancingPolicy.newQueryPlan(DefaultLoadBalancingPolicy.java:175)
    at com.datastax.oss.driver.internal.core.metadata.LoadBalancingPolicyWrapper.newQueryPlan(LoadBalancingPolicyWrapper.java:159)
    at com.datastax.oss.driver.internal.core.cql.CqlRequestHandler.onThrottleReady(CqlRequestHandler.java:213)
    at com.datastax.oss.driver.internal.core.session.throttling.PassThroughRequestThrottler.register(PassThroughRequestThrottler.java:54)
    at com.datastax.oss.driver.internal.core.cql.CqlRequestHandler.<init>(CqlRequestHandler.java:191)
    at com.datastax.oss.driver.internal.core.cql.CqlRequestAsyncProcessor.process(CqlRequestAsyncProcessor.java:46)
    at com.datastax.oss.driver.internal.core.cql.CqlRequestAsyncProcessor.process(CqlRequestAsyncProcessor.java:31)
    at com.datastax.oss.driver.internal.core.session.DefaultSession.execute(DefaultSession.java:241)
    ...
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.tuple.Pair
    ... 38 common frames omitted

Suggested Fix

Either:

  1. Add commons-lang3 as a runtime dependency in core/pom.xml:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.17.0</version>
</dependency>
  1. Or replace the usage with a simple two-element return type (e.g., an int[] or a private inner record/class) to avoid pulling in an extra transitive dependency for a single usage.

Workaround

Users can add commons-lang3 explicitly to their own project dependencies:

Maven:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.17.0</version>
</dependency>

sbt:

libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.17.0"

Environment

  • ScyllaDB Java Driver: 4.19.0.5
  • Java: 17+
  • Multi-node ScyllaDB cluster (not reproducible with single-node test setups)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions