flowable/flowable-engine · error · ActivitiIllegalArgumentException

process instance tenant id is null

Error message

process instance tenant id is null

What it means

HistoricProcessInstanceQuery.processInstanceTenantId(tenantId) validates that the exact-match tenant filter is non-null. Filtering historic process instances by a null tenant is unsupported, so the library throws ActivitiIllegalArgumentException immediately when building the query. Use processInstanceTenantIdWithoutTenant() for tenantless instances.

Solutions

  1. Pass an actual tenant id string.
  2. Use processInstanceTenantIdWithoutTenant() when you mean 'instances with no tenant'.
  3. Resolve the tenant before query building and fail with your own error if absent (e.g. throw IllegalArgumentException naming the missing header).
  4. Propagate tenant context into async executions and scheduled jobs explicitly.

Example fix

// before
query.processInstanceTenantId(secureHeader("X-Tenant")); // null if header absent
// after
String tenant = secureHeader("X-Tenant");
if (tenant == null) {
    throw new MissingTenantException("X-Tenant header required");
}
query.processInstanceTenantId(tenant);
Defensive patterns

Strategy: validation

Validate before calling

if (tenantId == null) throw new MissingTenantException("tenant id required for processInstanceTenantId()");

Type guard

boolean hasTenant(String t) { return t != null && !t.isEmpty(); }

Try / catch

try {
    query.processInstanceTenantId(tenantId);
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage().contains("tenant id is null")) {
        query.processInstanceTenantIdWithoutTenant();
    }
}

Prevention

When it happens

Trigger: Calling processInstanceTenantId(null) — usually a tenant variable resolved from an unauthenticated context, an unset process variable, or a missing request header.

Common situations: Multi-tenant apps where background jobs/schedulers run without tenant context; API consumers omitting the tenant header; code migrated from single-tenant versions that now passes null.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/HistoricProcessInstanceQueryImpl.java:368

    public HistoricProcessInstanceQuery limitProcessInstanceVariables(Integer processInstanceVariablesLimit) {
        this.processInstanceVariablesLimit = processInstanceVariablesLimit;
        return this;
    }

    public Integer getProcessInstanceVariablesLimit() {
        return processInstanceVariablesLimit;
    }

    @Override
    public HistoricProcessInstanceQuery withJobException() {
        this.withJobException = true;
        return this;
    }

    @Override
    public HistoricProcessInstanceQuery processInstanceTenantId(String tenantId) {
        if (tenantId == null) {
            throw new ActivitiIllegalArgumentException("process instance tenant id is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.tenantId = tenantId;
        } else {
            this.tenantId = tenantId;
        }
        return this;
    }

    @Override
    public HistoricProcessInstanceQuery processInstanceTenantIdLike(String tenantIdLike) {
        if (tenantIdLike == null) {
            throw new ActivitiIllegalArgumentException("process instance tenant id is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.tenantIdLike = tenantIdLike;
        } else {
            this.tenantIdLike = tenantIdLike;

View on GitHub (pinned to d6d39ce1c6)