flowable/flowable-engine · error · FlowableObjectNotFoundException

Process definition does not have a form defined: " +…

Error message

Process definition does not have a form defined: " + processDefinition.getId()

What it means

getStartForm looks up the form attached to a process definition (form key / form info). If the definition exists but no start form is defined, Flowable throws FlowableObjectNotFoundException rather than returning an empty form.

Solutions

  1. Add a flowable:formKey (or form definition reference) to the process start event and redeploy
  2. Deploy the referenced form model to the engine
  3. Treat 404 for startform as 'no form configured' in client code

Example fix

// before
<startEvent id="start" />
// after
<startEvent id="start" flowable:formKey="startForm" />
Defensive patterns

Strategy: try-catch

Validate before calling

const model = await getBpmnModel(defId);
const hasFormKey = model.startEvents.some(se => se.formKey != null);

Type guard

const hasStartForm = (def) => def != null && def.formKey != null;

Try / catch

try { form = await api.getStartForm(defId); }
catch (e) { if (e.status === 404) form = null; /* no form configured */ else throw e; }

Prevention

When it happens

Trigger: GET /repository/process-definitions/{id}/startform (formInfo) for a definition whose start event has no flowable:formKey / no form definition resolved.

Common situations: Models authored without a start-form key; form deployments removed or not deployed to the same engine; form resolution failing silently so formInfo stays null.

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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/repository/ProcessDefinitionResource.java:220

        FlowElement startElement = process.getInitialFlowElement();
        if (startElement instanceof StartEvent) {
            StartEvent startEvent = (StartEvent) startElement;
            if (StringUtils.isNotEmpty(startEvent.getFormKey())) {
                if (startEvent.isSameDeployment()) {
                    Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(processDefinition.getDeploymentId()).singleResult();
                    formInfo = formRepositoryService.getFormModelByKeyAndParentDeploymentId(startEvent.getFormKey(),
                    		deployment.getParentDeploymentId(), processDefinition.getTenantId(), processEngineConfiguration.isFallbackToDefaultTenant());
                    
                } else {
                    formInfo = formRepositoryService.getFormModelByKey(startEvent.getFormKey(), processDefinition.getTenantId(),
                            processEngineConfiguration.isFallbackToDefaultTenant());
                }
            }
        }

        if (formInfo == null) {
            // Definition found, but no form attached
            throw new FlowableObjectNotFoundException("Process definition does not have a form defined: " + processDefinition.getId());
        }
        
        return formInfo;
    }

    protected ProcessDefinitionResponse activateProcessDefinition(ProcessDefinition processDefinition, boolean suspendInstances, Date date) {
        if (!repositoryService.isProcessDefinitionSuspended(processDefinition.getId())) {
            throw new FlowableConflictException("Process definition with id '" + processDefinition.getId() + " ' is already active");
        }
        repositoryService.activateProcessDefinitionById(processDefinition.getId(), suspendInstances, date);

        ProcessDefinitionResponse response = restResponseFactory.createProcessDefinitionResponse(processDefinition);

        // No need to re-fetch the ProcessDefinition, just alter the suspended
        // state of the result-object
        response.setSuspended(false);
        return response;
    }

View on GitHub (pinned to d6d39ce1c6)