quarkusio/quarkus · error · IllegalStateException
${actualType} type can not be used to represent JWT claims i
Error message
${actualType} type can not be used to represent JWT claims in @Singleton or @ApplicationScoped beans, make the bean @RequestScoped or wrap this type with org.eclipse.microprofile.jwt.ClaimValue or jakarta.inject.Provider or jakarta.enterprise.inject.Instance What it means
When a JWT claim is injected (e.g. Optional<X> or raw X via @Claim) into a @Singleton or @ApplicationScoped bean, the value would be resolved once at bean creation outside any request, so it cannot represent a per-request claim. The extension's build step inspects such injection points and throws this IllegalStateException at build time, telling you to make the bean request-scoped or wrap the type in ClaimValue/Provider/Instance for lazy per-request resolution.
Source
Thrown at extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java:204
if (injectionPoint.hasDefaultedQualifier()) {
continue;
}
AnnotationInstance claimQualifier = injectionPoint.getRequiredQualifier(CLAIM_NAME);
if (claimQualifier != null) {
Type actualType = injectionPoint.getRequiredType();
Optional<BeanInfo> bean = injectionPoint.getTargetBean();
if (bean.isPresent()) {
DotName scope = bean.get().getScope().getDotName();
if (!REQUEST_SCOPED_NAME.equals(scope)
&& (!ALL_PROVIDER_NAMES.contains(injectionPoint.getType().name())
&& !CLAIM_VALUE_NAME.equals(actualType.name()))) {
String error = String.format(
"%s type can not be used to represent JWT claims in @Singleton or @ApplicationScoped beans"
+ ", make the bean @RequestScoped or wrap this type with org.eclipse.microprofile.jwt.ClaimValue"
+ " or jakarta.inject.Provider or jakarta.enterprise.inject.Instance",
actualType.name());
throw new IllegalStateException(error);
}
}
if (injectionPoint.getType().name().equals(DotNames.PROVIDER) && actualType.name().equals(DotNames.OPTIONAL)) {
additionalTypes.add(actualType);
}
}
}
// Register a custom bean
BeanConfigurator<Optional<?>> configurator = beanRegistrationPhase.getContext().configure(Optional.class);
for (Type type : additionalTypes) {
configurator.addType(type);
}
configurator.scope(BuiltinScope.DEPENDENT.getInfo());
configurator.qualifiers(AnnotationInstance.create(CLAIM_NAME, null,
new AnnotationValue[] { AnnotationValue.createStringValue("value", ""),View on GitHub (pinned to e1c734241f)
Solutions
- Make the bean @RequestScoped so the claim resolves per request
- Wrap the injected type with org.eclipse.microprofile.jwt.ClaimValue (e.g. ClaimValue<Optional<String>>)
- Wrap with jakarta.inject.Provider<T> or jakarta.enterprise.inject.Instance<T> to defer resolution to access time
Example fix
// before
@ApplicationScoped
public class ClaimService {
@Inject
@Claim(claim = "upn")
Optional<String> upn; // build-time failure
}
// after
@ApplicationScoped
public class ClaimService {
@Inject
ClaimValue<Optional<String>> upn; // resolved lazily per request
} Defensive patterns
Strategy: type-guard
Validate before calling
// Prefer ClaimValue/Provider/Instance when injecting claims into non-request-scoped beans @Inject ClaimValue<Optional<String>> upn; // safe in @ApplicationScoped
Type guard
// Guard: claims may only be injected directly in request-scoped beans
static boolean isDirectClaimInjectionSafe(Class<?> beanClass) {
return beanClass.isAnnotationPresent(jakarta.enterprise.context.RequestScoped.class)
|| beanClass.isAnnotationPresent(jakarta.enterprise.context.SessionScoped.class);
} Try / catch
try {
cdiContainer();
} catch (IllegalStateException e) {
if (e.getMessage().contains("can not be used to represent JWT claims")) {
throw new IllegalStateException("Use ClaimValue/Provider/Instance or make the bean @RequestScoped", e);
}
throw e;
} Prevention
- Default to ClaimValue<T> for claim injection in long-lived beans
- Never convert request-scoped claim-consuming beans to @Singleton without rewiring injections
- Search for @Claim fields when changing bean scopes
- Review ArC build warnings about wrong-scope injections
When it happens
Trigger: @Inject @Claim(claim = "...") Optional<String> claim (or a similar non-ClaimValue type) inside a bean annotated @Singleton or @ApplicationScoped; including cases where actualType is Optional (or non-ClaimValue) in such beans.
Common situations: Injecting claims into application-wide services (config holders, scheduled jobs); refactoring a @RequestScoped bean to @Singleton (or @ApplicationScoped) and forgetting the claim injection; copying claim-injection snippets into singleton REST resources.
Related errors
- @CachedResults class must be an interface or declare a no-ar
- %s type can not be used to represent JWT claims in @Singleto
- Not possible to define the scope %s for the REST client %s
- A receiver method must have exactly one parameter annotated
- A receiver method must not be private:
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/f7428048f6d2a603.
Report an issue: GitHub.