{"record":{"id":"e86740cdc9184fab","repo":"flowable/flowable-engine","slug":"form-property-id-is-required-e86740","errorCode":null,"errorMessage":"form property '${id}' is required","messagePattern":"form property '(.+?)' is required","errorType":"validation","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/form/FormPropertyHandler.java","lineNumber":84,"sourceCode":"        if (modelValue instanceof String) {\n            formProperty.setValue((String) modelValue);\n        } else if (type != null) {\n            String formValue = type.convertModelValueToFormValue(modelValue);\n            formProperty.setValue(formValue);\n        } else if (modelValue != null) {\n            formProperty.setValue(modelValue.toString());\n        }\n\n        return formProperty;\n    }\n\n    public void submitFormProperty(ExecutionEntity execution, Map<String, String> properties) {\n        if (!isWritable && properties.containsKey(id)) {\n            throw new ActivitiException(\"form property '\" + id + \"' is not writable\");\n        }\n\n        if (isRequired && !properties.containsKey(id) && defaultExpression == null) {\n            throw new ActivitiException(\"form property '\" + id + \"' is required\");\n        }\n        boolean propertyExists = false;\n        Object modelValue = null;\n        if (properties.containsKey(id)) {\n            propertyExists = true;\n            final String propertyValue = properties.remove(id);\n            if (type != null) {\n                modelValue = type.convertFormValueToModelValue(propertyValue);\n            } else {\n                modelValue = propertyValue;\n            }\n        } else if (defaultExpression != null) {\n            final Object expressionValue = defaultExpression.getValue(execution);\n            if (type != null && expressionValue != null) {\n                modelValue = type.convertFormValueToModelValue(expressionValue.toString());\n            } else if (expressionValue != null) {\n                modelValue = expressionValue.toString();\n            } else if (isRequired) {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/form/FormPropertyHandler.java#L66-L102","documentation":"FormPropertyHandler throws this when a form property declared as required (activiti:required=\"true\") is absent from the submitted properties map and no defaultExpression can supply a value. The engine refuses to complete the form submission so the process variable is never set from a missing value. It is a validation guard in submitFormProperty during form property submission.","triggerScenarios":"Calling FormService.submitTaskFormData(taskId, properties) or submitFormProperty with a map that omits an id whose FormPropertyHandler has isRequired=true and defaultExpression==null.","commonSituations":"UI form omits a required field; client sends only changed fields; process model was changed to mark a property required after clients were built; key typo means containsKey(id) is false even though the user filled something.","solutions":["Add the missing property key to the submitted properties map with a value","Set a defaultExpression on the form property in the BPMN XML so a missing value is acceptable","Remove the activiti:required attribute if the field is truly optional","Fix key/typo mismatch between the UI field name and the form property id"],"exampleFix":"// before\nformService.submitTaskFormData(taskId, Collections.singletonMap(\"comment\", \"ok\")); // 'amount' is required\n// after\nMap<String, String> props = new HashMap<>();\nprops.put(\"comment\", \"ok\");\nprops.put(\"amount\", \"100\");\nformService.submitTaskFormData(taskId, props);","handlingStrategy":"validation","validationCode":"Map<String,String> props = formService.getTaskFormModel(taskId) != null ? submitted : new HashMap<>();\nfor (FormInfo fi : formService.getTaskFormData(taskId).getFormProperties()) {\n    if (fi.isRequired() && !submitted.containsKey(fi.getId()) && fi.getDefaultExpression() == null)\n        throw new IllegalArgumentException(\"Missing required form property: \" + fi.getId());\n}","typeGuard":"boolean isComplete(Map<String,FormProperty> meta, Map<String,String> submitted) {\n    return meta.values().stream()\n        .filter(FormProperty::isRequired)\n        .allMatch(p -> submitted.containsKey(p.getId()) || p.getDefaultExpression() != null);\n}","tryCatchPattern":"try {\n    formService.submitTaskFormData(taskId, props);\n} catch (FlowableException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"is required\")) {\n        // surface missing-field error to the user\n    } else throw e;\n}","preventionTips":["Render the form from the engine's FormData metadata so required fields are always present","Validate all required ids client-side before submission","Re-check required flags after any BPMN model change","Submit full property maps, not sparse deltas"],"tags":["forms","validation","workflow"],"backgroundTag":"missing-required-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}