flowable/flowable-engine · error · FlowableException

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

Error message

There are ${results.size()} event definitions with key = '${channelDefinitionKey}' and version = '${eventVersion}' in tenant = '${tenantId}'.

What it means

Thrown by MybatisChannelDefinitionDataManager.findChannelDefinitionByKeyAndVersionAndTenantId when selectChannelDefinitionsByKeyAndVersionAndTenantId returns multiple rows for the same key, version, and tenantId. Key+version+tenant should be unique; duplicates indicate inconsistent persisted channel definition data.

Solutions

  1. Deduplicate: keep one FLW_EV_CHANNEL_DEFINITION row per (KEY_, VERSION_, TENANT_ID_) and delete the rest.
  2. Guard tenant-scoped deployments with a lock so the same key/version is inserted only once per tenant.
  3. Add a unique constraint on (TENANT_ID_, KEY_, VERSION_) in the event registry schema.
  4. Make startup deployment code idempotent (check existence for the tenant before deploying).

Example fix

// before
channelRepositoryService.createDeployment().addClasspathResource(res).tenantId(tenantId).deploy(); // runs on every node
// after
if (channelRepositoryService.createChannelDefinitionQuery().channelKey(key)
        .channelDefinitionVersion(1).tenantId(tenantId).count() == 0) {
    channelRepositoryService.createDeployment().addClasspathResource(res).tenantId(tenantId).deploy();
}
Defensive patterns

Strategy: validation

Validate before calling

long n = eventRepositoryService.createChannelDefinitionQuery()
    .channelKey(key).channelDefinitionVersion(version).tenantId(tenantId).count();
if (n > 1) throw new IllegalStateException("duplicate tenant channel definitions for " + key + " v" + version + " tenant " + tenantId);

Try / catch

try {
    ch = manager.findChannelDefinitionByKeyAndVersionAndTenantId(key, version, tenantId);
} catch (FlowableException e) {
    ch = dedupeTenantRowsAndRetry(key, version, tenantId);
}

Prevention

When it happens

Trigger: Concurrent deployment of the same channel model for the same tenant on multiple nodes, both inserting a row with identical KEY_/VERSION_/TENANT_ID_.

Common situations: Clustered deployments without deployment locking; tenant-aware environments re-deploying the same resource on every node at startup; manual data fixes creating copies.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/persistence/entity/data/impl/MybatisChannelDefinitionDataManager.java:133

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

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

    @Override
    @SuppressWarnings("unchecked")
    public List<ChannelDefinition> findChannelDefinitionsByNativeQuery(Map<String, Object> parameterMap) {
        return getDbSqlSession().selectListWithRawParameter("selectChannelDefinitionByNativeQuery", parameterMap);
    }

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

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

View on GitHub (pinned to d6d39ce1c6)