quarkusio/quarkus · error · IllegalStateException

Failed to instantiate {{VIRTUAL_THREAD_BINDER_CLASSNAME}}

Error message

Failed to instantiate {{VIRTUAL_THREAD_BINDER_CLASSNAME}}

What it means

VirtualThreadCollector collects metrics for virtual-thread-per-request scheduling by instantiating Micrometer's virtual-thread binder reflectively via Class.forName(VIRTUAL_THREAD_BINDER_CLASSNAME). The binder class only exists when the JDK/Micrometer build supports it; any failure to load or construct it (ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException) is wrapped in an IllegalStateException.

Source

Thrown at extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/virtualthreads/VirtualThreadCollector.java:74

            }
        } else {
            this.tags = List.of();
        }
        this.binder = instantiated;
    }

    /**
     * Use reflection to avoid calling a class touching Java 21+ APIs.
     *
     * @param tags the tags.
     * @return the binder, {@code null} if the instantiation failed.
     */
    public MeterBinder instantiate(List<Tag> tags) {
        try {
            Class<?> clazz = Class.forName(VIRTUAL_THREAD_BINDER_CLASSNAME);
            return (MeterBinder) clazz.getDeclaredConstructor(Iterable.class).newInstance(tags);
        } catch (Exception e) {
            throw new IllegalStateException("Failed to instantiate " + VIRTUAL_THREAD_BINDER_CLASSNAME, e);
        }
    }

    private Tag createTagFromEntry(String entry) {
        String[] parts = entry.trim().split("=");
        if (parts.length == 2) {
            return Tag.of(parts[0], parts[1]);
        } else {
            throw new IllegalStateException("Invalid tag: " + entry + " (expected key=value)");
        }
    }

    public MeterBinder getBinder() {
        return binder;
    }

    public List<Tag> getTags() {
        return tags;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Upgrade Micrometer (and Quarkus) to a version where VirtualThreadBinder exists and your JDK is 21+; disable the binder otherwise (quarkus.micrometer.binder.virtual-threads.enabled=false)
  2. Check the configured class name for typos and confirm the fully-qualified class is on the classpath in the target Micrometer version
  3. If building native, register the binder class for reflection (quarkus.native.additional-build-args with --reflect-config or @RegisterForReflection)
  4. Inspect the wrapped cause (e.getCause()) in the IllegalStateException stack trace to distinguish ClassNotFoundException vs constructor failure

Example fix

// before (application.properties on JDK 17, Micrometer without the binder)
quarkus.micrometer.binder.virtual-threads.enabled=true
// -> IllegalStateException: Failed to instantiate ...

// after: run on JDK 21+ with a Micrometer that has the binder, or disable
# quarkus.micrometer.binder.virtual-threads.enabled=false
Defensive patterns

Strategy: try-catch

Validate before calling

String cls = "io.micrometer.core.instrument.binder.jvm.VirtualThreadBinder";
boolean binderAvailable;
try {
    Class.forName(cls);
    binderAvailable = Runtime.version().feature() >= 21;
} catch (ClassNotFoundException e) {
    binderAvailable = false;
}
// only enable quarkus.micrometer.binder.virtual-threads.enabled if binderAvailable

Type guard

static boolean virtualThreadBinderSupported() {
    try {
        Class.forName("io.micrometer.core.instrument.binder.jvm.VirtualThreadBinder");
        return true;
    } catch (ClassNotFoundException e) {
        return false;
    }
}

Try / catch

try {
    MeterBinder binder = collector.instantiate(tags);
} catch (IllegalStateException e) {
    log.warnf("Virtual thread metrics unavailable: %s (cause=%s)", e.getMessage(), e.getCause());
    // proceed without virtual thread metrics
}

Prevention

When it happens

Trigger: quarkus.micrometer.binder.virtual-threads.enabled=true (or a binder class name configured) while running on a JDK without support, or with a Micrometer version that lacks/renamed the binder class, or where the class exists but its (Iterable) constructor fails.

Common situations: Enabling virtual thread metrics on JDK 19/20 previews or a Micrometer version older than 1.10.x where the VirtualThreadBinder did not exist; a custom VIRTUAL_THREAD_BINDER_CLASSNAME typo; running native-image where the reflectively-loaded class was not registered for reflection.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/0153074e30add448. Report an issue: GitHub.