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
- Add a flowable:formKey (or form definition reference) to the process start event and redeploy
- Deploy the referenced form model to the engine
- 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
- Set flowable:formKey on start events when forms are required
- Deploy form models together with process definitions
- Treat 404 on startform as 'no form' rather than an unexpected fault
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
- Attachment with id ' ' does not have content associated…
- Case definition does not have a start form defined
- Could not find a app model json with id
- Could not find a dead letter job with id
- Could not find a deployment with id
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)