{"record":{"id":"9db51d9f8ca6a651","repo":"flowable/flowable-engine","slug":"custom-properties-resolver-expression-expressi","errorCode":null,"errorMessage":"Custom properties resolver expression \" + expression + \" did not return a Map<String, Object>","messagePattern":"Custom properties resolver expression \" \\+ expression \\+ \" did not return a Map<String, Object>","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/ExpressionCustomPropertiesResolver.java","lineNumber":42,"sourceCode":" * An {@link CustomPropertiesResolver} that evaluates a {@link Expression} when notified.\n * \n * @author Yvo Swillens\n */\npublic class ExpressionCustomPropertiesResolver implements CustomPropertiesResolver {\n\n    protected Expression expression;\n\n    public ExpressionCustomPropertiesResolver(Expression expression) {\n        this.expression = expression;\n    }\n\n    @Override\n    public Map<String, Object> getCustomPropertiesMap(DelegateExecution execution) {\n        Object expressionValue = expression.getValue(execution);\n        if (expressionValue instanceof Map) {\n            return (Map<String, Object>) expressionValue;\n        } else {\n            throw new FlowableIllegalArgumentException(\"Custom properties resolver expression \" + expression + \" did not return a Map<String, Object>\");\n        }\n    }\n\n    /**\n     * returns the expression text for this execution listener. Comes in handy if you want to check which listeners you already have.\n     */\n    public String getExpressionText() {\n        return expression.getExpressionText();\n    }\n}\n","sourceCodeStart":24,"sourceCodeEnd":53,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/listener/ExpressionCustomPropertiesResolver.java#L24-L53","documentation":"ExpressionCustomPropertiesResolver evaluates a Flowable expression and expects it to resolve to a Map<String, Object> of custom properties. When the expression returns anything else (String, null, List, etc.), getCustomPropertiesMap throws FlowableIllegalArgumentException because the resolved value cannot be used as a custom properties map.","triggerScenarios":"A custom properties resolver is configured in BPMN as an expression (e.g. flowable:customPropertiesResolverExpression=\"${someBean}\") and the referenced bean/method returns a non-Map value, or the expression fails to resolve and returns null.","commonSituations":"Developer points the customPropertiesResolver expression at a bean method returning a List or POJO instead of Map<String, Object>; typo in expression causing null return; copy-pasted execution-listener expression that returns a String.","solutions":["Change the expression target so it returns a java.util.Map<String, Object> (e.g. bean method returning Map<String, Object>)","If the bean returns a POJO, add a wrapper method that converts it to a Map before returning","Verify the expression resolves (log or unit-test expression.getValue(execution)) — null usually means a typo or missing bean"],"exampleFix":"// before\npublic String getProperties() { return \"a=1\"; }\n// after\npublic Map<String, Object> getProperties() {\n    Map<String, Object> props = new HashMap<>();\n    props.put(\"a\", 1);\n    return props;\n}","handlingStrategy":"validation","validationCode":"Object value = ((ExpressionCustomPropertiesResolver) resolver).getExpression().getValue(execution);\nif (!(value instanceof Map)) {\n    throw new IllegalStateException(\"Resolver expression must return Map<String, Object>, got: \" + (value == null ? \"null\" : value.getClass().getName()));\n}","typeGuard":"boolean isValidResolver(Object v) { return v instanceof Map<?, ?>; }","tryCatchPattern":"try {\n    Map<String, Object> props = resolver.getCustomPropertiesMap(execution);\n} catch (FlowableIllegalArgumentException e) {\n    log.warn(\"Custom properties resolver returned non-Map: {}\", e.getMessage());\n}","preventionTips":["Type resolver bean methods as Map<String, Object> explicitly","Unit-test expressions with a mock DelegateExecution before deploying","Avoid reusing execution-listener expressions for property resolvers"],"tags":["bpmn","expression","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}