flowable/flowable-engine · error · FlowableObjectNotFoundException

No decision found for key

Error message

No decision found for key <decisionKey>. There was also no fall back decision found for default tenant <defaultTenant>.

What it means

Thrown when a decision is resolved by key and tenantId, fallback-to-default-tenant is enabled, the default tenant resolves to a non-empty value, but findLatestDecisionByKeyAndTenantId still finds no Decision for that default tenant. The engine treats this as a hard not-found because even the fallback could not locate the decision.

Solutions

  1. Check what DefaultTenantProvider returns and confirm decisions are actually deployed under that tenant; deploy the .dmn resource with that tenantId if not.
  2. Fix or replace the defaultTenantProvider so it returns the tenant that owns the decision.
  3. Verify the decision key is correct via DmnRepositoryService decision query.
  4. Disable fallbackToDefaultTenant if the decision should only resolve by the explicitly given tenantId.

Example fix

// before
dmnEngineConfiguration.setDefaultTenantProvider((tenantId, scope, key) -> "missing-tenant");
// after
dmnEngineConfiguration.setDefaultTenantProvider((tenantId, scope, key) ->
    dmnRepositoryService.createDecisionQuery().decisionKey(key).list().isEmpty() ? null : tenantId != null ? tenantId : "default");
Defensive patterns

Strategy: validation

Validate before calling

String defaultTenant = defaultTenantProvider.getDefaultTenant(tenantId, ScopeTypes.DMN, key);
if (defaultTenant != null && dmnRepositoryService.createDecisionQuery().decisionKey(key).tenantId(defaultTenant).count() == 0) {
    log.error("Decision {} not deployed under default tenant {}", key, defaultTenant);
}

Try / catch

try { dmnEngine.executeDecision(builder.decisionKey(key).tenantId(tenant)); }
catch (FlowableObjectNotFoundException e) { throw new IllegalStateException("Decision " + key + " missing even under default tenant", e); }

Prevention

When it happens

Trigger: Executing a decision with decisionKey and tenantId where (isFallbackToDefaultTenant || config fallback) is true, DefaultTenantProvider returns a non-empty default tenant, and no Decision row exists for key+defaultTenant.

Common situations: Decision never deployed under the default tenant; default tenant provider misconfigured to a tenant that has no DMN deployments; decision deployed only to a custom tenant while the fallback resolves to the default one.

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/bb4339b2e7120ee5. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/cmd/AbstractExecuteDecisionCmd.java:148

                // If there is no decision table found linked to the deployment id, try to find one without a specific deployment id.
                decision = decisionEntityManager.findLatestDecisionByKey(decisionKey);

                if (decision == null) {
                    throw new FlowableObjectNotFoundException("No decision found for key: " + decisionKey +
                        " and parent deployment id " + parentDeploymentId +
                        ". There was also no fall back decision found without parent deployment id.");
                }
            }
            
        } else if (StringUtils.isNotEmpty(decisionKey) && StringUtils.isNotEmpty(tenantId)) {
            decision = decisionEntityManager.findLatestDecisionByKeyAndTenantId(decisionKey, tenantId);
            if (decision == null) {
                if (executeDecisionContext.isFallbackToDefaultTenant() || dmnEngineConfiguration.isFallbackToDefaultTenant()) {
                    String defaultTenant = dmnEngineConfiguration.getDefaultTenantProvider().getDefaultTenant(tenantId, ScopeTypes.DMN, decisionKey);
                    if (StringUtils.isNotEmpty(defaultTenant)) {
                        decision = decisionEntityManager.findLatestDecisionByKeyAndTenantId(decisionKey, defaultTenant);
                        if (decision == null) {
                            throw new FlowableObjectNotFoundException("No decision found for key: " + decisionKey +
                                ". There was also no fall back decision found for default tenant " +
                                defaultTenant + ".");
                        }

                    } else {
                        decision = decisionEntityManager.findLatestDecisionByKey(decisionKey);
                        if (decision == null) {
                            throw new FlowableObjectNotFoundException("No decision found for key: " + decisionKey +
                                ". There was also no fall back decision found without tenant.");
                        }
                    }

                } else {
                    throw new FlowableObjectNotFoundException(
                        "No decision found for key: " + decisionKey + " and tenantId: " + tenantId + ".");
                }
            }

View on GitHub (pinned to d6d39ce1c6)