flowable/flowable-engine · error · FlowableException

There are process definitions with key = '' and version =…

Error message

There are  process definitions with key = '' and version = '' in tenant=''.

What it means

findProcessDefinitionByKeyAndVersionAndTenantId expects exactly one process definition for the given key, version and tenant id. If the query returns multiple rows, the tenant-scoped definition data is duplicated/inconsistent, so a FlowableException is thrown with the count, key, version and tenant id.

Solutions

  1. Query ACT_RE_PROCDEF with KEY_, VERSION_ and TENANT_ID_ filters, remove duplicates keeping a single canonical row.
  2. Redeploy cleanly for that tenant to regenerate a consistent definition row, then migrate/repoint any instances referencing the duplicate.
  3. Guard deployment tooling so only the engine assigns versions; never INSERT into ACT_RE_PROCDEF manually.
  4. If duplicates are pervasive, restore ACT_RE_PROCDEF (and related tables) from a consistent backup.

Example fix

// before
// duplicates silently present for tenant 'acme'
// after
// DELETE FROM ACT_RE_PROCDEF WHERE KEY_='order' AND VERSION_=3 AND TENANT_ID_='acme' AND ID_ <> '<canonical-id>';
Defensive patterns

Strategy: validation

Validate before calling

long n = repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(tenantId).processDefinitionKey(key).processDefinitionVersion(ver).count();
if (n > 1) throw new IllegalStateException("Duplicate definitions for tenant=" + tenantId + " key=" + key + " version=" + ver);

Prevention

When it happens

Trigger: Tenant-aware definition resolution (deployments or runtime lookups by key+version+tenantId) when ACT_RE_PROCDEF holds duplicate rows for the same KEY_, VERSION_ and TENANT_ID_, typically after manual data changes or environment merges.

Common situations: Multi-tenant setups where the same definition was (re)inserted manually per tenant; database merges restoring duplicate rows; a deployment bug in old versions racing on tenant-scoped versioning; case where tenant rows also collide with the default-tenant rows.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/data/impl/MybatisProcessDefinitionDataManager.java:150

            return results.get(0);
        } else if (results.size() > 1) {
            throw new FlowableException("There are " + results.size() + " process definitions with key = '" + processDefinitionKey + "' and version = '" + processDefinitionVersion + "'.");
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public ProcessDefinitionEntity findProcessDefinitionByKeyAndVersionAndTenantId(String processDefinitionKey, Integer processDefinitionVersion, String tenantId) {
        Map<String, Object> params = new HashMap<>();
        params.put("processDefinitionKey", processDefinitionKey);
        params.put("processDefinitionVersion", processDefinitionVersion);
        params.put("tenantId", tenantId);
        List<ProcessDefinitionEntity> results = getDbSqlSession().selectList("selectProcessDefinitionsByKeyAndVersionAndTenantId", params);
        if (results.size() == 1) {
            return results.get(0);
        } else if (results.size() > 1) {
            throw new FlowableException("There are " + results.size() + " process definitions with key = '" + processDefinitionKey + "' and version = '" + processDefinitionVersion + "' in tenant='" + tenantId + "'.");
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<ProcessDefinition> findProcessDefinitionsByNativeQuery(Map<String, Object> parameterMap) {
        return getDbSqlSession().selectListWithRawParameter("selectProcessDefinitionByNativeQuery", parameterMap);
    }

    @Override
    public long findProcessDefinitionCountByNativeQuery(Map<String, Object> parameterMap) {
        return (Long) getDbSqlSession().selectOne("selectProcessDefinitionCountByNativeQuery", parameterMap);
    }

    @Override
    public void updateProcessDefinitionTenantIdForDeployment(String deploymentId, String newTenantId) {
        HashMap<String, Object> params = new HashMap<>();

View on GitHub (pinned to d6d39ce1c6)