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
- Resolve the current authenticated user first and only call startableByUser with a non-null id.
- If authorization is not needed, omit the startableByUser filter rather than passing null.
- 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
- Require authentication before building authorization-filtered queries
- Never pass identity-lookup results without a null check
- In tests, stub the security context instead of leaving it null
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
- userId is null and groups are null or empty
- callback id is null
- callbackId is null
- case definition tenantId is null
- caseDefinition tenantId is null
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)