flowable/flowable-engine · error · FlowableIllegalArgumentException

task tenant id is null

Error message

task tenant id is null

What it means

taskTenantId(String) throws FlowableIllegalArgumentException when the tenantId argument is null. Flowable requires an explicit non-null tenant id string for tenant-scoped queries; use taskWithoutTenantId() or omit the filter to match tasks without a tenant.

Source

Thrown at modules/flowable-task-service/src/main/java/org/flowable/task/service/impl/HistoricTaskInstanceQueryImpl.java:1984

            this.involvedGroups = involvedGroups;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery ignoreAssigneeValue() {
        if (inOrStatement) {
            this.currentOrQueryObject.ignoreAssigneeValue = true;
        } else {
            this.ignoreAssigneeValue = true;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery taskTenantId(String tenantId) {
        if (tenantId == null) {
            throw new FlowableIllegalArgumentException("task tenant id is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.tenantId = tenantId;
        } else {
            this.tenantId = tenantId;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery taskTenantIdLike(String tenantIdLike) {
        if (tenantIdLike == null) {
            throw new FlowableIllegalArgumentException("task tenant id is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.tenantIdLike = tenantIdLike;
        } else {
            this.tenantIdLike = tenantIdLike;

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Pass a non-null tenant id; resolve/validate the tenant from the request context first.
  2. To query tasks with no tenant, use taskWithoutTenantId() instead of passing null.
  3. If tenant filtering is optional, call taskTenantId() only when a tenant id is present.
  4. Note Flowable stores missing tenants as empty string (""); pass "" if you intentionally filter that value, or prefer taskWithoutTenantId().

Example fix

// before
query.taskTenantId(tenantContext.getTenantId()); // throws when null

// after
String tenantId = tenantContext.getTenantId();
if (tenantId != null) {
    query.taskTenantId(tenantId);
} else {
    query.taskWithoutTenantId();
}
Defensive patterns

Strategy: validation

Validate before calling

if (tenantId != null && !tenantId.isEmpty()) {
    historicTaskInstanceQuery.taskTenantId(tenantId);
} else {
    historicTaskInstanceQuery.taskWithoutTenantId(); // or omit the filter
}

Type guard

boolean isValidTenant(String tenantId) {
    return tenantId != null && !tenantId.isEmpty();
}

Try / catch

try {
    query.taskTenantId(tenantId);
} catch (FlowableIllegalArgumentException e) {
    log.warn("Invalid tenantId filter: {}", e.getMessage());
    query.taskWithoutTenantId();
}

Prevention

When it happens

Trigger: Calling taskTenantId(null) — usually a tenant resolved from a multi-tenant context that is null (e.g. no tenant in the session/HTTP header), or a task.getTenantId() on a task created before tenant support.

Common situations: Multi-tenancy setups where the tenant resolver returns null for global/admin users or when the tenant header is missing; forwarding a task's tenantId when tasks were deployed without one (empty string vs null confusion).

Related errors


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