flowable/flowable-engine · error · FlowableObjectNotFoundException

Process definition with key '${processDefinitionKey}' and te

Error message

Process definition with key '${processDefinitionKey}' and tenantId '${tenantId}' was not found

What it means

Thrown by resolveProcessDefinition when a specific tenantId was supplied and no process definition exists with that key AND tenantId. Unlike the fallback case, no default-tenant fallback was requested or it was disabled, so the engine reports the exact key+tenant combination that was not found.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/util/ProcessInstanceHelper.java:429

            if (parentDeploymentId != null) {
                processDefinition = processDefinitionEntityManager
                        .findProcessDefinitionByParentDeploymentAndKey(parentDeploymentId, processDefinitionKey);
            }
            if (processDefinition == null) {
                processDefinition = processDefinitionEntityManager
                        .findLatestProcessDefinitionByKey(processDefinitionKey);
            }
        }

        if (processDefinition == null) {
            if (tenantId == null || ProcessEngineConfiguration.NO_TENANT_ID.equals(tenantId)) {
                throw new FlowableObjectNotFoundException(
                        "No process definition found for key '" + processDefinitionKey + "'", ProcessDefinition.class);
            } else if (fallbackToDefaultTenant) {
                throw new FlowableObjectNotFoundException(
                        "No process definition found for key '" + processDefinitionKey + "'. Fallback to default tenant was also applied.", ProcessDefinition.class);
            } else {
                throw new FlowableObjectNotFoundException(
                        "Process definition with key '" + processDefinitionKey + "' and tenantId '" + tenantId + "' was not found", ProcessDefinition.class);
            }
        }

        return processDefinition;
    }

    public void callCaseInstanceStateChangeCallbacks(CommandContext commandContext, ProcessInstance processInstance, String oldState, String newState) {
        if (processInstance.getCallbackId() != null && processInstance.getCallbackType() != null) {
            Map<String, List<RuntimeInstanceStateChangeCallback>> caseInstanceCallbacks = CommandContextUtil
                    .getProcessEngineConfiguration(commandContext).getProcessInstanceStateChangedCallbacks();

            if (caseInstanceCallbacks != null && caseInstanceCallbacks.containsKey(processInstance.getCallbackType())) {
                for (RuntimeInstanceStateChangeCallback caseInstanceCallback : caseInstanceCallbacks.get(processInstance.getCallbackType())) {

                    caseInstanceCallback.stateChanged(new CallbackData(processInstance.getCallbackId(), 
                        processInstance.getCallbackType(), processInstance.getId(), oldState, newState));

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Confirm the exact tenantId used at deployment time and pass that same value to the start call.
  2. Deploy the definition for the tenant if missing: createDeployment().tenantId(tenantId).addClasspathResource(...).deploy().
  3. List where the key exists: repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).list() and read each definition's tenantId.
  4. Enable fallbackToDefaultTenant if falling back to the default tenant is acceptable in your setup.

Example fix

// before
runtimeService.startProcessInstanceByKey("order", "ACME", vars); // deployed as "acme"
// after
runtimeService.startProcessInstanceByKey("order", "acme", vars); // match deployment tenantId exactly
Defensive patterns

Strategy: validation

Validate before calling

ProcessDefinition def = repositoryService.createProcessDefinitionQuery()
    .processDefinitionKey(key).processDefinitionTenantId(tenantId).latestVersion().singleResult();
if (def == null) {
    throw new IllegalStateException("No definition key='" + key + "' tenantId='" + tenantId + "' — deploy it first");
}

Try / catch

try {
    return runtimeService.startProcessInstanceByKey(key, tenantId, vars);
} catch (FlowableObjectNotFoundException e) {
    if (e.getMessage().contains("tenantId '" + tenantId + "' was not found")) {
        repositoryService.createDeployment().addClasspathResource("processes/" + key + ".bpmn20.xml").tenantId(tenantId).deploy();
        return runtimeService.startProcessInstanceByKey(key, tenantId, vars);
    }
    throw e;
}

Prevention

When it happens

Trigger: startProcessInstanceByKey(key, tenantId, ...) variants (or resolveProcessDefinition called with fallbackToDefaultTenant=false) when the tenant never had this process deployed, including cases where the definition exists only under another tenant or the empty tenant.

Common situations: Wrong tenant id passed by configuration/env-var; process deployed with no tenant while the caller passes one; tenant ids case-mismatched; starting from an event/message path where the tenant was taken from a different source (e.g. user context) than the deployment.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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