eclipse-vertx/vert.x · error · IllegalStateException

Cannot find service on the classpath or module path

Error message

Cannot find service on the classpath or module path

What it means

ServiceHelper.loadFactory(ServiceLoader) checks a supplied ServiceLoader for any provider instance and throws IllegalStateException('Cannot find service on the classpath or module path') when none is found. Same missing-SPI condition as the class-based variant, but resolution is done by the caller's ServiceLoader (supports module path).

Source

Thrown at vertx-core/src/main/java/io/vertx/core/impl/ServiceHelper.java:66

  public static <T> List<T> loadFactories(Class<T> clazz, ClassLoader classLoader) {
    if (classLoader != null) {
      return loadFactories(ServiceLoader.load(clazz, classLoader));
    }
    classLoader = Thread.currentThread().getContextClassLoader();
    List<T> list = loadFactories(ServiceLoader.load(clazz, classLoader));
    if (list.isEmpty() && classLoader != ServiceHelper.class.getClassLoader()) {
      // By default, ServiceLoader.load uses the TCCL, this may not be enough in environment dealing with
      // classloaders differently such as OSGi. So we should try to use the  classloader having loaded this
      // class. In OSGi it would be the bundle exposing vert.x and so have access to all its classes.
      list = loadFactories(ServiceLoader.load(clazz, ServiceHelper.class.getClassLoader()));
    }
    return list;
  }

  public static <T> T loadFactory(ServiceLoader<T> factories) {
    T factory = loadFactoryOrNull(factories);
    if (factory == null) {
      throw new IllegalStateException("Cannot find service on the classpath or module path");
    }
    return factory;
  }

  public static <T> T loadFactoryOrNull(ServiceLoader<T> factories) {
    Collection<T> collection = loadFactories(factories);
    if (!collection.isEmpty()) {
      return collection.iterator().next();
    } else {
      return null;
    }
  }

  public static <T> List<T> loadFactories(ServiceLoader<T> factories) {
    List<T> list = new ArrayList<>();
    if (factories.iterator().hasNext()) {
      factories.iterator().forEachRemaining(list::add);
      return list;

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Add a META-INF/services/<fully.qualified.Spi> file naming the implementation, or a `provides Spi with Impl;` clause in module-info.java
  2. Verify the implementation jar is on the classpath/module path at runtime
  3. Check the services file name exactly matches the SPI interface FQN (no whitespace/typos)
  4. Use loadFactoryOrNull to handle absence gracefully

Example fix

// before
// module-info.java has no provides clause
requires io.vertx.core;
// after
requires io.vertx.core;
provides io.vertx.core.spi.cluster.ClusterManagerFactory
    with io.vertx.spi.cluster.hazelcast.HazelcastClusterManagerFactory;
Defensive patterns

Strategy: fallback

Validate before calling

boolean present = ServiceLoader.load(Spi.class).iterator().hasNext();
if (!present) throw new IllegalStateException("No provider for " + Spi.class.getName());

Try / catch

try { impl = ServiceHelper.loadFactory(loader); } catch (IllegalStateException e) { impl = defaultImpl(); }

Prevention

When it happens

Trigger: Creating a ServiceLoader.load(SomeSpi.class) that yields zero providers and passing it to loadFactory(loader) — no 'provides' in module-info and no META-INF/services entry.

Common situations: JPMS modularized apps missing `provides SomeSpi with SomeImpl`; classpath apps whose jar lacks the services descriptor; typos in the fully-qualified provider class name in META-INF/services/<interface>.

Understand the failure class

Background: "X is not installed. Please install it with pip install Y": missing optional dependency errors — ImportError/ValueError raised when a library's optional extra was never installed — this error's family across 22 libraries.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/a41b13dc00a4c2b9. Report an issue: GitHub.