flowable/flowable-engine · error · FlowableIllegalArgumentException

userId is null

Error message

userId is null

What it means

startableByUser(String) restricts a ProcessDefinitionQuery to definitions the given user can start. Flowable throws FlowableIllegalArgumentException when userId is null since start-authorization filtering requires a concrete user identity.

Solutions

  1. Resolve and pass the actual Flowable user id of the potential starter.
  2. Skip the startableByUser call (and any authorization filter) for unauthenticated/anonymous listings.
  3. Fail fast earlier with a clear 'authentication required' error instead of the query-time argument error.

Example fix

// before
String userId = SecurityUtils.getCurrentUserId();
return repositoryService.createProcessDefinitionQuery().startableByUser(userId).list();

// after
String userId = SecurityUtils.getCurrentUserId();
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
if (userId != null) {
    query.startableByUser(userId);
}
return query.list();
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try { query.startableByUser(userId); } catch (FlowableIllegalArgumentException e) { throw new AuthenticationRequiredException("user must be authenticated to filter by starter"); }

Prevention

When it happens

Trigger: Calling startableByUser(null), usually when the current authenticated user is not resolved (anonymous session, missing security context) and the user id variable is null.

Common situations: Endpoints exposed without authentication; task/list UIs showing definitions before login; identities stored in an external IdP where the Flowable user id mapping is absent.

Related errors


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

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/ProcessDefinitionQueryImpl.java:360

        this.eventSubscriptionName = eventName;
        return this;
    }

    public Collection<String> getAuthorizationGroups() {
        if (authorizationGroupsSet) {
            // if authorizationGroupsSet is true then startableByUserOrGroups was called
            // and the groups passed in that methods have precedence
            return authorizationGroups;
        } else if (authorizationUserId == null) {
            return null;
        }
        return CommandContextUtil.getProcessEngineConfiguration().getCandidateManager().getGroupsForCandidateUser(authorizationUserId);
    }

    @Override
    public ProcessDefinitionQueryImpl startableByUser(String userId) {
        if (userId == null) {
            throw new FlowableIllegalArgumentException("userId is null");
        }
        this.authorizationUserId = userId;
        return this;
    }

    @Override
    public ProcessDefinitionQuery 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)