quarkusio/quarkus · error · IllegalStateException

Unable to locate proper constructor for dynamically register

Error message

Unable to locate proper constructor for dynamically registered provider. Make sure the class has a no-args constructor and that it uses '@Context' for field injection if necessary.

What it means

Quarkus replaces RESTEasy's default injector with QuarkusInjectorFactory, which relies on build-time generated constructors for JAX-RS providers. When a provider is registered dynamically at runtime (not discovered during the build), Quarkus cannot produce a synthetic constructor, so the constructor passed in is null and this IllegalStateException is thrown. It signals that the provider class cannot be instantiated by the Quarkus-supported mechanism (no-args constructor plus @Context field injection).

Source

Thrown at extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java:30

import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.PropertyInjector;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.metadata.ResourceClass;
import org.jboss.resteasy.spi.metadata.ResourceConstructor;

import io.quarkus.arc.ClientProxy;

public class QuarkusInjectorFactory extends InjectorFactoryImpl {

    private static final Logger log = Logger.getLogger("io.quarkus.resteasy.runtime");

    @SuppressWarnings("rawtypes")
    @Override
    public ConstructorInjector createConstructor(Constructor constructor, ResteasyProviderFactory providerFactory) {
        if (constructor == null) {
            throw new IllegalStateException(
                    "Unable to locate proper constructor for dynamically registered provider. Make sure the class has a no-args constructor and that it uses '@Context' for field injection if necessary.");
        }
        log.debugf("Create constructor: %s", constructor);
        return new QuarkusConstructorInjector(constructor, super.createConstructor(constructor, providerFactory));
    }

    @Override
    public ConstructorInjector createConstructor(ResourceConstructor constructor, ResteasyProviderFactory providerFactory) {
        log.debugf("Create resource constructor: %s", constructor.getConstructor());
        return new QuarkusConstructorInjector(constructor.getConstructor(),
                super.createConstructor(constructor, providerFactory));
    }

    @SuppressWarnings("rawtypes")
    @Override
    public PropertyInjector createPropertyInjector(Class resourceClass, ResteasyProviderFactory providerFactory) {
        PropertyInjector delegate = super.createPropertyInjector(resourceClass, providerFactory);
        return new UnwrappingPropertyInjector(delegate);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Register the provider statically: annotate it with @Provider so it is discovered at build time, or list it via quarkus.resteasy.filter.classes / features config properties.
  2. Give the provider class a public no-args constructor and inject JAX-RS context objects (UriInfo, HttpHeaders, Providers) via @Context fields instead of constructor parameters.
  3. If dynamic registration is required, register an instance created by the caller only when the provider supports no-arg construction; otherwise refactor to use CDI (@Inject) so Quarkus wires it.

Example fix

// before: dynamic registration with constructor injection
public class MyFilter implements ContainerRequestFilter {
    private final SomeService service;
    public MyFilter(SomeService service) { this.service = service; }
}
factory.register(new MyFilter(service)); // throws at runtime

// after: build-time discovered, @Context injection
@Provider
public class MyFilter implements ContainerRequestFilter {
    @Context
    Providers providers;
    @Inject
    SomeService service; // via CDI-aware setup, or use CDI.current().select(SomeService.class).get()
    public MyFilter() {}
}
Defensive patterns

Strategy: validation

Validate before calling

static <T> void checkProviderUsable(Class<T> providerClass) throws IntrospectionException {
    boolean hasNoArgCtor = Arrays.stream(providerClass.getConstructors())
            .anyMatch(c -> c.getParameterCount() == 0);
    if (!hasNoArgCtor)
        throw new IllegalArgumentException(providerClass.getName() + " needs a public no-arg constructor for Quarkus/RESTEasy dynamic registration");
}

Type guard

static boolean isQuarkusRegistrable(Class<?> c) {
    try { return c.getConstructor() != null; } catch (NoSuchMethodException e) { return false; }
}

Try / catch

try {
    factory.register(MyFilter.class);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("Unable to locate proper constructor")) {
        log.warn("Falling back to static @Provider registration for " + MyFilter.class);
        // register via build-time discovery instead
    } else throw e;
}

Prevention

When it happens

Trigger: Calling ResteasyProviderFactory.register(...)/registerProvider(...) with a provider class instance whose Constructor is null at createConstructor time — i.e. a provider registered programmatically at runtime rather than discovered at build time, typically one requiring constructor injection.

Common situations: Registering a ContainerRequestFilter, WriterInterceptor, ReaderInterceptor, MessageBodyReader/Writer, or ExceptionMapper via the ResteasyProviderFactory at runtime (e.g. in a Feature or filter chain) instead of via @Provider class discovery or quarkus.resteasy.* config; classes with only parameterized constructors.

Related errors


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