spring-projects/spring-security · error · IllegalArgumentException

Expression was null but expected boolean result '{expression

Error message

Expression was null but expected boolean result '{expressionString}'

What it means

Spring Security's ExpressionUtils.evaluateAsBoolean evaluates a SpEL security expression (e.g. from @PreAuthorize) expecting a Boolean result. If the expression evaluates to null instead of true/false, the library throws IllegalArgumentException because authorization requires a definitive boolean answer. This typically happens when the expression returns a null value (e.g. calling a method or accessing a property that yields null).

Source

Thrown at core/src/main/java/org/springframework/security/access/expression/ExpressionUtils.java:32

 * limitations under the License.
 */

package org.springframework.security.access.expression;

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;

public final class ExpressionUtils {

	private ExpressionUtils() {
	}

	public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) {
		try {
			Boolean result = expr.getValue(ctx, Boolean.class);
			if (result == null) {
				throw new IllegalArgumentException(
						"Expression was null but expected boolean result '" + expr.getExpressionString() + "'");
			}
			return result;
		}
		catch (EvaluationException ex) {
			throw new IllegalArgumentException("Failed to evaluate expression '" + expr.getExpressionString() + "'",
					ex);
		}
	}

}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Change the security expression so it always returns a boolean, e.g. wrap null-able calls: @bean.findUser() != null and @bean.findUser().active
  2. Use null-safe SpEL operators (?.) in the expression so comparisons yield true/false instead of null
  3. If you own the called method, return Boolean.FALSE instead of null when the check fails
  4. Catch IllegalArgumentException around the authorization call and treat it as a denial with a clearer message

Example fix

// before
@PreAuthorize("@permService.checkAccess(#doc)")
// after
@PreAuthorize("@permService.checkAccess(#doc) == true")  // or make checkAccess return boolean, never null
Defensive patterns

Strategy: validation

Validate before calling

Boolean val = expr.getValue(ctx, Boolean.class);
if (val == null) throw new IllegalStateException("Expression '" + expr.getExpressionString() + "' must not return null");

Type guard

if (expr.getValue(ctx) instanceof Boolean b) { /* safe to authorize */ }

Try / catch

try { ExpressionUtils.evaluateAsBoolean(expr, ctx); }
catch (IllegalArgumentException e) { denyAccess(); }

Prevention

When it happens

Trigger: Calling ExpressionUtils.evaluateAsBoolean(expr, ctx) where expr.getValue(ctx, Boolean.class) returns null — e.g. a @PreAuthorize expression like "@bean.method()" whose return value is null, or a property access resolving to null.

Common situations: Custom authorization expressions invoking service/bean methods that return null instead of boolean; security expressions referencing missing properties on the root object; refactoring a check from 'hasRole(...)' to a custom check that returns null on some paths.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/f6080c35c6a6496d. Report an issue: GitHub.