flowable/flowable-engine · error · FlowableIllegalArgumentException

The target tenant id must not be null.

Error message

The target tenant id must not be null.

What it means

Flowable throws FlowableIllegalArgumentException when constructing a ChangeTenantIdBuilderImpl with a null targetTenantId. The builder moves data between tenants, so both source and target tenant ids must be valid strings. This is an eager constructor argument validation to fail fast before any data operations.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/tenant/ChangeTenantIdBuilderImpl.java:35

import org.flowable.common.engine.api.tenant.ChangeTenantIdResult;

/**
 * @author Filip Hrisafov
 */
public class ChangeTenantIdBuilderImpl implements ChangeTenantIdBuilder {

    protected final String sourceTenantId;
    protected final String targetTenantId;
    protected final ChangeTenantIdManager changeTenantIdManager;

    protected String definitionTenantId;

    public ChangeTenantIdBuilderImpl(String sourceTenantId, String targetTenantId, ChangeTenantIdManager changeTenantIdManager) {
        if (sourceTenantId == null) {
            throw new FlowableIllegalArgumentException("The source tenant id must not be null.");
        }
        if (targetTenantId == null) {
            throw new FlowableIllegalArgumentException("The target tenant id must not be null.");
        }
        this.sourceTenantId = sourceTenantId;
        this.targetTenantId = targetTenantId;
        if (sourceTenantId.equals(targetTenantId)) {
            throw new FlowableIllegalArgumentException("The source and the target tenant ids must be different.");
        }
        this.changeTenantIdManager = changeTenantIdManager;
    }

    @Override
    public ChangeTenantIdBuilder definitionTenantId(String definitionTenantId) {
        if (definitionTenantId == null) {
            throw new FlowableIllegalArgumentException("definitionTenantId must not be null");
        }
        this.definitionTenantId = definitionTenantId;
        return this;
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the target tenant id string is non-null before calling changeTenantId(source, target)
  2. Check that the config/property/request parameter supplying the target tenant is actually populated
  3. Validate inputs at the application boundary and reject the operation early with a clear message

Example fix

// before
managementService.changeTenantId("tenantA", targetTenant);
// after
if (targetTenant == null) throw new IllegalArgumentException("targetTenant is required");
managementService.changeTenantId("tenantA", targetTenant);
Defensive patterns

Strategy: validation

Validate before calling

if (sourceTenantId == null || targetTenantId == null) {
    throw new IllegalArgumentException("source and target tenant ids are required");
}

Type guard

boolean hasTenantIds(String s, String t) {
    return s != null && !s.trim().isEmpty() && t != null && !t.trim().isEmpty();
}

Try / catch

try {
    managementService.changeTenantId(sourceTenantId, targetTenantId);
} catch (FlowableIllegalArgumentException e) {
    logger.error("Invalid tenant change request: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Calling ProcessEngineManagementService (or engine-specific) changeTenantId(sourceTenantId, null), or passing a null variable that flows into the new ChangeTenantIdBuilderImpl(source, target, manager) constructor.

Common situations: Tenant id read from config or request parameter that is null/missing; dynamically resolved tenant id not set in a multi-tenant deployment.

Related errors


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