spring-projects/spring-security · error · ClassCastException
securityContextResult + " is not assignable to " + parameter
Error message
securityContextResult + " is not assignable to " + parameter.getParameterType()
What it means
Thrown by CurrentSecurityContextArgumentResolver when a @CurrentSecurityContext annotated parameter cannot receive the resolved security context object because its declared Java type is not assignable from the actual result's class. Spring Security throws this (as ClassCastException) only when errorOnInvalidType() is true on the annotation; otherwise it silently returns null.
Source
Thrown at web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java:170
this.scanner = SecurityAnnotationScanners.requireUnique(CurrentSecurityContext.class, templateDefaults);
}
private @Nullable Object resolveSecurityContextFromAnnotation(MethodParameter parameter,
CurrentSecurityContext annotation, SecurityContext securityContext) {
Object securityContextResult = securityContext;
String expressionToParse = annotation.expression();
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(securityContext);
context.setVariable("this", securityContext);
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse);
securityContextResult = expression.getValue(context);
}
if (securityContextResult != null
&& !parameter.getParameterType().isAssignableFrom(securityContextResult.getClass())) {
if (annotation.errorOnInvalidType()) {
throw new ClassCastException(
securityContextResult + " is not assignable to " + parameter.getParameterType());
}
return null;
}
return securityContextResult;
}
/**
* Obtain the specified {@link Annotation} on the specified {@link MethodParameter}.
* @param parameter the {@link MethodParameter} to search for an {@link Annotation}
* @return the {@link Annotation} that was found or null.
*/
private @Nullable CurrentSecurityContext findMethodAnnotation(MethodParameter parameter) {
if (this.useAnnotationTemplate) {
return this.scanner.scan(parameter.getParameter());
}
CurrentSecurityContext annotation = parameter.getParameterAnnotation(this.annotationType);
if (annotation != null) {View on GitHub (pinned to 96852e8860)
Solutions
- Change the parameter's declared type to a supertype actually produced by the expression (e.g. Authentication, SecurityContext, Object) and downcast safely in the body
- Align the annotation's expression with the parameter type, e.g. @CurrentSecurityContext(expression = "authentication.principal") with parameter type matching the actual principal class
- If a null is acceptable on mismatch, set @CurrentSecurityContext(errorOnInvalidType = false) so the resolver returns null instead of throwing
- Check what the expression actually returns (SecurityContextHolder.getContext().getAuthentication().getClass()) and fix the mismatch
Example fix
// before
public Me me(@CurrentSecurityContext(expression = "authentication.principal") CustomUser user) { ... }
// after
public Me me(@CurrentSecurityContext(expression = "authentication.principal") Object principal) {
CustomUser user = (principal instanceof CustomUser u) ? u : null;
...
} Defensive patterns
Strategy: validation
Validate before calling
Object result = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!parameterType.isAssignableFrom(result.getClass())) {
// fix annotation expression or parameter type before deployment
} Type guard
static <T> T safePrincipal(Class<T> type) {
Object p = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return type.isInstance(p) ? type.cast(p) : null;
} Try / catch
try {
return resolver.resolveArgument(param, null, request, null);
} catch (ClassCastException e) {
log.warn("@CurrentSecurityContext type mismatch for {}: {}", param, e.getMessage());
return null; // or map to 400
} Prevention
- Declare @CurrentSecurityContext parameters as Authentication, SecurityContext, or Object unless the expression guarantees the concrete type
- Match the SpEL expression to the parameter type (authentication.principal for UserDetails types)
- Use errorOnInvalidType = false when a null is an acceptable outcome
- Add an integration test resolving the annotation for each Authentication implementation you support
When it happens
Trigger: A controller/handler method declares @CurrentSecurityContext SomeType param, the SpEL expression (default 'authentication' or a custom expression) evaluates to an object whose class is not assignable to SomeType (e.g. expression='authentication' but parameter type is CustomUserDetails, or expression returns Authentication but parameter is String).
Common situations: Declaring a parameter as a concrete UserDetails implementation while the principal is a different type; using a custom SpEL expression whose result type differs from the parameter type; copy-pasting resolvers across apps with different Authentication implementations; Spring Security 5.x where errorOnInvalidType defaults changed semantics for expression-based resolution.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- principal + " is not assignable to " + parameter.getParamete
- principal + " is not assignable to " + parameter.getParamete
- Access is denied
- Filter target must be a collection, array, map or stream typ
- server_error
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/4c89479c75a9e9ed.
Report an issue: GitHub.