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

  1. Add the form resource (matching the formKey exactly) to the process deployment
  2. Correct the formKey in the BPMN XML to match the deployed resource name including path and case
  3. 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

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


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)