{"record":{"id":"30e65417205a8341","repo":"flowable/flowable-engine","slug":"the-attribute-must-resolve-to-a-string-resolv","errorCode":null,"errorMessage":"The [<attribute>] must resolve to a String. Resolved to [<class>] for [<value>]","messagePattern":"The \\[<attribute>\\] must resolve to a String\\. Resolved to \\[<class>\\] for \\[<value>\\]","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/kafka/KafkaChannelDefinitionProcessor.java","lineNumber":526,"sourceCode":"        } else if (resolved instanceof Boolean) {\n            result = (Boolean) resolved;\n        } else if (resolved != null) {\n            throw new IllegalStateException(\n                    \"The [\" + attribute + \"] must resolve to a Boolean or a String that can be parsed as a Boolean. \"\n                            + \"Resolved to [\" + resolved.getClass() + \"] for [\" + value + \"]\");\n        }\n        return result;\n    }\n\n    protected String resolveExpressionAsString(String value, String attribute) {\n        if (!StringUtils.hasLength(value)) {\n            return null;\n        }\n        Object resolved = resolveExpression(value);\n        if (resolved instanceof String) {\n            return (String) resolved;\n        } else {\n            throw new IllegalStateException(\"The [\" + attribute + \"] must resolve to a String. \"\n                + \"Resolved to [\" + resolved.getClass() + \"] for [\" + value + \"]\");\n        }\n    }\n\n    protected Collection<String> resolveTopics(KafkaInboundChannelModel channelDefinition) {\n        Collection<String> topics = channelDefinition.getTopics();\n\n        if (topics == null || topics.isEmpty()) {\n            return Collections.emptyList();\n        }\n\n        List<String> resultTopics = new ArrayList<>();\n\n        for (String queue : topics) {\n            resolveTopics(resolveExpression(queue), resultTopics, channelDefinition);\n        }\n\n        return resultTopics;","sourceCodeStart":508,"sourceCodeEnd":544,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/kafka/KafkaChannelDefinitionProcessor.java#L508-L544","documentation":"resolveExpressionAsString resolves a channel attribute and requires it to be null or a String; anything else (Number, Boolean, collection, bean) throws IllegalStateException naming the attribute, resolved class, and original value. It is used for topic, groupId, and other string channel settings, so almost any mis-typed Kafka channel attribute can surface here.","triggerScenarios":"Any String attribute of the Kafka channel model (topic, groupId, properties values, sameIntervalTopicReuseStrategy, listener endpoint fields) resolves via an expression to a non-String object during createKafkaListenerEndpoint or resolveProperties/resolveRetryConfiguration.","commonSituations":"groupId set to a numeric literal in the channel model (groupId: 12345 resolves to Integer); topic name all digits resolved as Number by SpEL; property placeholder returning an Integer from YAML; passing a raw object instead of a string in programmatic model building.","solutions":["Quote the value so it resolves as a String, e.g. groupId: \"12345\" in YAML/JSON model.","Check SpEL evaluation: use '...' literals for numeric-looking topic/group names.","Fix the backing property type to String in configuration/beans.","If programmatic, call String.valueOf(...) when building the channel model attribute."],"exampleFix":"// before (channel model JSON/YAML)\n\"groupId\": 12345\n\n// after\n\"groupId\": \"12345\"","handlingStrategy":"type-guard","validationCode":"Object v = resolveExpression(value);\nif (v != null && !(v instanceof String)) throw new IllegalArgumentException(attribute + \" must resolve to a String, got \" + v.getClass());","typeGuard":"boolean isStringLike(Object v) { return v == null || v instanceof String; }","tryCatchPattern":"try { processor.createKafkaListenerEndpoint(...); } catch (IllegalStateException e) { if (e.getMessage().contains(\"must resolve to a String\")) { log.error(\"Channel attribute must be a String: {}\", e.getMessage()); } else throw e; }","preventionTips":["Quote all channel model string values, especially numeric topic/group names","Use SpEL string literals ('...') for expressions","Type backing config properties as String","Validate channel models with a schema before deployment"],"tags":["kafka","type-mismatch","string","expression-resolution"],"backgroundTag":"type-mismatch","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"}