flowable/flowable-engine · error · ActivitiObjectNotFoundException
Form with formKey ' ' does not exist
Error message
Form with formKey '${formKey}' does not exist What it means
JuelFormEngine.getFormTemplateString looks up a deployment resource whose name equals the formKey. When no resource with that name exists in the deployment, it throws ActivitiObjectNotFoundException. The JUEL form engine resolves forms as deployment resources, so the formKey must exactly match a resource name.
Solutions
- Add the form resource (matching the formKey exactly) to the process deployment
- Correct the formKey in the BPMN XML to match the deployed resource name including path and case
- Deploy the form in the same deployment as the process definition or verify the deploymentId used for lookup
Example fix
// before (BPMN)
<startFormKey>forms/approve.form</startFormKey> // not deployed
// after: add forms/approve.form to the deployment repository
repositoryService.createDeployment().addClasspathResource("forms/approve.form").addClasspathResource("process.bpmn20.xml").deploy(); Defensive patterns
Strategy: validation
Validate before calling
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).latestVersion().singleResult(); Resource r = repositoryService.createDeploymentQuery().deploymentId(pd.getDeploymentId()).singleResult() != null ? null : null; boolean formExists = repositoryService.getResourceAsStream(pd.getDeploymentId(), formKey) != null; // throws if missing
Type guard
boolean formResourceExists(DeploymentBuilder b, String formKey) {
return ((DeploymentBuilderImpl) b).getResources().keySet().contains(formKey);
} Try / catch
try {
Object form = formService.getRenderedTaskForm(taskId);
} catch (FlowableObjectNotFoundException e) {
if (e.getMessage() != null && e.getMessage().contains("does not exist")) { /* fall back to default form */ }
else throw e;
} Prevention
- Deploy the form template in the same deployment as the BPMN
- Keep formKey strings in sync with resource paths (path + case-sensitive)
- Verify resources with repositoryService.getResourceAsStream after deployment
When it happens
Trigger: Rendering a task form via FormService.getRenderedTaskForm(formKey) / getRenderedStartForm where formKey points to a resource name that is not in the process definition's deployment.
Common situations: Form template file forgotten from the deployment bar; formKey typo or path mismatch (e.g. 'forms/approval.form' vs 'approval.form'); form deployed in a different deployment than the process definition; case-sensitive name mismatch.
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
- Could not find a resource with id
- deployment does not exist:
- deployment does not exist
- deployment does not exist
- inputStream for resource
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/748d639fcf70dc5c.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/form/JuelFormEngine.java:66
if (taskForm.getFormKey() == null) {
return null;
}
String formTemplateString = getFormTemplateString(taskForm, taskForm.getFormKey());
ScriptingEngines scriptingEngines = Context.getProcessEngineConfiguration().getScriptingEngines();
TaskEntity task = (TaskEntity) taskForm.getTask();
return scriptingEngines.evaluate(formTemplateString, ScriptingEngines.DEFAULT_SCRIPTING_LANGUAGE, task.getExecution());
}
protected String getFormTemplateString(FormData formInstance, String formKey) {
String deploymentId = formInstance.getDeploymentId();
ResourceEntity resourceStream = Context
.getCommandContext()
.getResourceEntityManager()
.findResourceByDeploymentIdAndResourceName(deploymentId, formKey);
if (resourceStream == null) {
throw new ActivitiObjectNotFoundException("Form with formKey '" + formKey + "' does not exist", String.class);
}
return new String(resourceStream.getBytes(), StandardCharsets.UTF_8);
}
}
View on GitHub (pinned to d6d39ce1c6)