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
- 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.
- 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.
- 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
- Always give JAX-RS providers a public no-args constructor
- Use @Context field injection instead of constructor injection in providers
- Prefer @Provider build-time discovery over runtime ResteasyProviderFactory.register calls in Quarkus
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
- More than one Application class: ${jakartaRestApplicationCla
- Usage of '@Inject' is not allowed in 'jakarta.ws.rs.core.App
- Unable to handle class: ${applicationClass}
- Extra steps left over
- Unsupported value type: %s
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/6e40284e60485328.
Report an issue: GitHub.