flowable/flowable-engine · error · FlowableIllegalArgumentException

userId is null

Error message

userId is null

What it means

startableByUser in CaseDefinitionQueryImpl throws FlowableIllegalArgumentException with "userId is null" when the userId argument is null. The filter restricts case definitions to those startable by the given user, so a null user is meaningless and rejected eagerly.

Solutions

  1. Resolve the current authenticated user first and only call startableByUser with a non-null id.
  2. If authorization is not needed, omit the startableByUser filter rather than passing null.
  3. Return 401/403 to the caller when no user is present instead of building the query.

Example fix

// before
query.startableByUser(securityService.getCurrentUser().getId());

// after
User user = securityService.getCurrentUser();
if (user != null) {
    query.startableByUser(user.getId());
} else {
    throw new SecurityException("authenticated user required");
}
Defensive patterns

Strategy: validation

Validate before calling

String userId = securityService.getCurrentUserId();
if (userId != null) {
    query.startableByUser(userId);
}

Type guard

boolean isAuthenticated(Principal p) { return p != null && p.getName() != null; }

Try / catch

try {
    query.startableByUser(userId);
} catch (FlowableIllegalArgumentException e) {
    if (!e.getMessage().contains("userId is null")) throw e;
    throw new SecurityException("user must be authenticated");
}

Prevention

When it happens

Trigger: Calling startableByUser(null) — commonly when the authenticated user could not be resolved (anonymous request, missing security context, failed identity lookup).

Common situations: Security context not populated because authentication was skipped in tests; passing the result of a user lookup that returns null for unknown users; running outside a web session (batch jobs) where no user exists.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/407218d492405726. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/repository/CaseDefinitionQueryImpl.java:311

        this.withLocalizationFallback = true;
        return this;
    }

    public Collection<String> getAuthorizationGroups() {
        // if authorizationGroupsSet is true then startableByUserOrGroups was called
        // and the groups passed in that methods have precedence
        if (authorizationGroupsSet) {
            return authorizationGroups;
        } else if (authorizationUserId == null) {
            return null;
        }
        return CommandContextUtil.getCmmnEngineConfiguration().getCandidateManager().getGroupsForCandidateUser(authorizationUserId);
    }
    
    @Override
    public CaseDefinitionQuery startableByUser(String userId) {
        if (userId == null) {
            throw new FlowableIllegalArgumentException("userId is null");
        }
        this.authorizationUserId = userId;
        return this;
    }

    @Override
    public CaseDefinitionQuery startableByUserOrGroups(String userId, Collection<String> groups) {
        if (userId == null && (groups == null || groups.isEmpty())) {
            throw new FlowableIllegalArgumentException("userId is null and groups are null or empty");
        }
        this.authorizationUserId = userId;
        this.authorizationGroups = groups;
        this.authorizationGroupsSet = true;
        return this;
    }

    // sorting ////////////////////////////////////////////

View on GitHub (pinned to d6d39ce1c6)