flowable/flowable-engine · error · FlowableIllegalArgumentException

user id is null

Error message

user id is null

What it means

FlowableIllegalArgumentException thrown by HistoricCaseInstanceQueryImpl.startedBy(String) when the userId argument is null. The 'started by' filter is an identity restriction on the historic case instance initiator; a null identity is not a valid query criterion, so the library rejects it at build time.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/HistoricCaseInstanceQueryImpl.java:648

    @Override
    public HistoricCaseInstanceQueryImpl startedAfter(Date afterTime) {
        if (afterTime == null) {
            throw new FlowableIllegalArgumentException("after time is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.startedAfter = afterTime;
        } else {
            this.startedAfter = afterTime;
        }

        return this;
    }

    @Override
    public HistoricCaseInstanceQueryImpl startedBy(String userId) {
        if (userId == null) {
            throw new FlowableIllegalArgumentException("user id is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.startedBy = userId;
        } else {
            this.startedBy = userId;
        }

        return this;
    }

    @Override
    public HistoricCaseInstanceQueryImpl finishedBy(String userId) {
        if (userId == null) {
            throw new FlowableIllegalArgumentException("user id is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.finishedBy = userId;
        } else {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Pass a valid non-null userId string to startedBy().
  2. If the user context may be anonymous, check for null and either skip the filter or throw a domain-level 'authentication required' error.
  3. Validate request-supplied user identifiers before constructing the query.

Example fix

// before
query.startedBy(Authentication.getAuthenticatedUserId()); // null when not logged in

// after
String userId = Authentication.getAuthenticatedUserId();
if (userId != null) {
    query.startedBy(userId);
}
Defensive patterns

Strategy: validation

Validate before calling

String userId = Authentication.getAuthenticatedUserId();
if (userId != null) {
    query.startedBy(userId);
}

Type guard

boolean hasUserId(String userId) { return userId != null && !userId.trim().isEmpty(); }

Try / catch

try {
    query.startedBy(userId);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("user id is null")) {
        throw new SecurityException("Cannot filter by initiator: no authenticated user");
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling createHistoricCaseInstanceQuery().startedBy(null); also occurs inside or() blocks since the null check precedes the inOrStatement branch.

Common situations: Passing the result of Authentication.getAuthenticatedUserId() (null for anonymous/unauthenticated contexts) into startedBy(); forwarding an optional 'userId' request parameter that was never provided.

Related errors


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