Describe the bug
The CPU fallback path in LuceneAcceleratedHNSWScalarQuantizedVectorsFormat and LuceneAcceleratedHNSWBinaryQuantizedVectorsFormat is broken. When cuVS resources cannot be created, both log GPU based indexing not supported, falling back to using the ... and then throw instead of falling back.
Steps to reproduce
Try loading the codecs
package com.nvidia.cuvs.repro;
import static com.nvidia.cuvs.lucene.ThreadLocalCuVSResourcesProvider.setCuVSResourcesInstance;
import static org.apache.lucene.index.VectorSimilarityFunction.EUCLIDEAN;
import com.nvidia.cuvs.lucene.AcceleratedHNSWParams;
import com.nvidia.cuvs.lucene.Lucene101AcceleratedHNSWCodec;
import com.nvidia.cuvs.lucene.LuceneAcceleratedHNSWBinaryQuantizedCodec;
import com.nvidia.cuvs.lucene.LuceneAcceleratedHNSWScalarQuantizedCodec;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Random;
import org.apache.lucene.codecs.Codec;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.KnnFloatVectorField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
/**
* Indexes a few vectors with each accelerated HNSW codec while cuVS resources are unavailable.
*
* <p>All three codecs are documented to fall back to CPU index construction in this state.
* Only {@code Lucene101AcceleratedHNSWCodec} does; the two quantized variants throw.
*/
public class FallbackRepro {
private static final int NUM_DOCS = 16;
private static final int DIMENSION = 8;
public static void main(String[] args) throws Exception {
// Force the !isSupported() branch on this thread, the same way
// TestCagraToHnswSerializationAndSearchWithFallbackWriter does.
setCuVSResourcesInstance(null);
AcceleratedHNSWParams params = new AcceleratedHNSWParams.Builder().build();
index("Lucene101AcceleratedHNSWCodec", new Lucene101AcceleratedHNSWCodec(params));
index("LuceneAcceleratedHNSWScalarQuantizedCodec", new LuceneAcceleratedHNSWScalarQuantizedCodec(params));
index("LuceneAcceleratedHNSWBinaryQuantizedCodec", new LuceneAcceleratedHNSWBinaryQuantizedCodec(params));
}
private static void index(String label, Codec codec) {
Random random = new Random(222);
IndexWriterConfig config = new IndexWriterConfig().setCodec(codec);
try {
Path indexPath = Files.createTempDirectory("cuvs-fallback-repro");
try (Directory directory = FSDirectory.open(indexPath);
IndexWriter writer = new IndexWriter(directory, config)) {
for (int i = 0; i < NUM_DOCS; i++) {
float[] vector = new float[DIMENSION];
for (int j = 0; j < DIMENSION; j++) {
vector[j] = random.nextFloat() * 100;
}
Document document = new Document();
document.add(new KnnFloatVectorField("vector_field", vector, EUCLIDEAN));
writer.addDocument(document);
}
}
System.out.printf("%-45s OK%n", label);
} catch (Throwable t) {
System.out.printf("%-45s %s%n", label, t);
}
}
}
output:
WARNING: Java vector incubator module is not readable. For optimal vector performance, pass '--add-modules jdk.incubator.vector' to enable Vector API.
Sep 16, 2026 5:38:43 PM com.nvidia.cuvs.lucene.Lucene99AcceleratedHNSWVectorsFormat fieldsWriter
WARNING: GPU based indexing not supported, falling back to using the Lucene99HnswVectorsWriter
Lucene101AcceleratedHNSWCodec OK
Sep 16, 2026 5:38:44 PM com.nvidia.cuvs.lucene.LuceneAcceleratedHNSWScalarQuantizedVectorsFormat fieldsWriter
WARNING: GPU based indexing not supported, falling back to using the Lucene99HnswScalarQuantizedVectorsFormat
Sep 16, 2026 5:38:44 PM com.nvidia.cuvs.lucene.LuceneProvider getLuceneHnswScalarQuantizedVectorsFormatInstance
SEVERE: Unable to initialize LuceneHnswScalarQuantizedVectorsFormat: class org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat cannot be cast to class org.apache.lucene.codecs.hnsw.FlatVectorsFormat (org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat and org.apache.lucene.codecs.hnsw.FlatVectorsFormat are in unnamed module of loader org.codehaus.mojo.exec.URLClassLoaderBuilder$ExecJavaClassLoader @5f7989fa)
LuceneAcceleratedHNSWScalarQuantizedCodec java.lang.ClassCastException: class org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat cannot be cast to class org.apache.lucene.codecs.hnsw.FlatVectorsFormat (org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat and org.apache.lucene.codecs.hnsw.FlatVectorsFormat are in unnamed module of loader org.codehaus.mojo.exec.URLClassLoaderBuilder$ExecJavaClassLoader @5f7989fa)
Sep 16, 2026 5:38:44 PM com.nvidia.cuvs.lucene.LuceneAcceleratedHNSWBinaryQuantizedVectorsFormat fieldsWriter
WARNING: GPU based indexing not supported, falling back to using the Lucene102HnswBinaryQuantizedVectorsFormat
Sep 16, 2026 5:38:44 PM com.nvidia.cuvs.lucene.LuceneProvider getLuceneHnswBinaryQuantizedVectorsFormatInstance
SEVERE: Unable to initialize LuceneBinaryQuantizedVectorsFormat: Cannot invoke "java.lang.Class.getConstructor(java.lang.Class[])" because "this.hnswBinaryQuantizedVectorsFormat" is null
LuceneAcceleratedHNSWBinaryQuantizedCodec java.lang.NullPointerException: Cannot invoke "java.lang.Class.getConstructor(java.lang.Class[])" because "this.hnswBinaryQuantizedVectorsFormat" is null
Additional context covers three things:
The NullPointerException is already tracked in #2615 as part of another set of issues.
Describe the bug
The CPU fallback path in
LuceneAcceleratedHNSWScalarQuantizedVectorsFormatandLuceneAcceleratedHNSWBinaryQuantizedVectorsFormatis broken. When cuVS resources cannot be created, both logGPU based indexing not supported, falling back to using the ...and then throw instead of falling back.Steps to reproduce
Try loading the codecs
output:
Additional context covers three things:
The
NullPointerExceptionis already tracked in #2615 as part of another set of issues.