quarkusio/quarkus · error · IllegalStateException

No TCCL available

Error message

No TCCL available

What it means

Arc's AbstractGenerator.gizmo(ClassOutput) reads the current thread context classloader to configure bytecode generation. If Thread.currentThread().getContextClassLoader() returns null, it throws IllegalStateException because the generator needs a classloader to operate.

Source

Thrown at independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AbstractGenerator.java:33

    static final String UNDERSCORE = "_";
    static final String SYNTHETIC_SUFFIX = "Synthetic";

    protected final boolean generateSources;
    protected final ReflectionRegistration reflectionRegistration;

    public AbstractGenerator(boolean generateSources, ReflectionRegistration reflectionRegistration) {
        this.generateSources = generateSources;
        this.reflectionRegistration = reflectionRegistration;
    }

    public AbstractGenerator(boolean generateSources) {
        this(generateSources, null);
    }

    static Gizmo gizmo(ClassOutput classOutput) {
        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        if (tccl == null) {
            throw new IllegalStateException("No TCCL available");
        }
        return Gizmo.create(classOutput)
                .withDebugInfo(false)
                .withParameters(false)
                .withLambdasAsAnonymousClasses(true);
    }

    /**
     * Generates a class name from a target package, base name and suffix. When the class
     * is located in a default package, the target package name is an empty string.
     *
     * @param targetPackage name of the target package
     * @param baseName simple class name
     * @param suffix suffix to append to the generated name
     * @return generated name
     */
    static String generatedNameFromTarget(String targetPackage, String baseName, String suffix) {
        if (targetPackage == null || targetPackage.isEmpty()) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Set the TCCL before invoking: Thread.currentThread().setContextClassLoader(YourClass.class.getClassLoader())
  2. Run generation on a thread that inherits the application/build classloader
  3. Wrap the call in a block that saves and restores an explicit classloader

Example fix

// before
Gizmo gizmo = AbstractGenerator.gizmo(classOutput); // TCCL null -> throws
// after
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(AbstractGenerator.class.getClassLoader());
try {
    Gizmo gizmo = AbstractGenerator.gizmo(classOutput);
} finally {
    Thread.currentThread().setContextClassLoader(old);
}
Defensive patterns

Strategy: validation

Validate before calling

if (Thread.currentThread().getContextClassLoader() == null) Thread.currentThread().setContextClassLoader(AbstractGenerator.class.getClassLoader());

Prevention

When it happens

Trigger: Calling Gizmo.gizmo(classOutput) from a thread whose context classloader has not been set (null TCCL).

Common situations: Threads spawned manually in build steps without inheriting/setting a TCCL; running generation in unusual environments (custom executors, native-image tooling) where TCCL is unset.

Related errors


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