{"record":{"id":"9c1008f5b43035e0","repo":"apache/pulsar","slug":"unsupported-non-primitive-optional-s-for-s","errorCode":null,"errorMessage":"unsupported non-primitive Optional<%s> for %s","messagePattern":"unsupported non-primitive Optional<(.+?)> for (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/util/FieldParser.java","lineNumber":191,"sourceCode":"        requireNonNull(field);\n        // if field is not primitive type\n        Type fieldType = field.getGenericType();\n        if (fieldType instanceof ParameterizedType) {\n            Class<?> clazz = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];\n            if (field.getType().equals(List.class)) {\n                // convert to list\n                return stringToList(strValue, clazz);\n            } else if (field.getType().equals(Set.class)) {\n                // covert to set\n                return stringToSet(strValue, clazz);\n            } else if (field.getType().equals(Map.class)) {\n                Class<?> valueClass =\n                    (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1];\n                return stringToMap(strValue, clazz, valueClass);\n            } else if (field.getType().equals(Optional.class)) {\n                Type typeClazz = ((ParameterizedType) fieldType).getActualTypeArguments()[0];\n                if (typeClazz instanceof ParameterizedType) {\n                    throw new IllegalArgumentException(format(\"unsupported non-primitive Optional<%s> for %s\",\n                            typeClazz.getClass(), field.getName()));\n                }\n                @SuppressWarnings(\"unchecked\") // typeClazz is verified to be a non-parameterized Class\n                Optional<?> result = Optional.ofNullable(convert(strValue, (Class<?>) typeClazz));\n                return result;\n            } else {\n                throw new IllegalArgumentException(\n                        format(\"unsupported field-type %s for %s\", field.getType(), field.getName()));\n            }\n        } else {\n            return convert(strValue, field.getType());\n        }\n    }\n\n    /**\n     * Sets the empty/null value if field is allowed to be set empty.\n     *\n     * @param strValue","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/util/FieldParser.java#L173-L209","documentation":"FieldParser.value() supports Optional<T> fields only when T is a simple (non-parameterized) type, because it converts the string with a single-class converter. If the Optional's type argument is itself parameterized (e.g. Optional<List<String>>, Optional<Map<K,V>>), it throws IllegalArgumentException. Note the message prints typeClazz.getClass() (the Type implementation class), not the type itself.","triggerScenarios":"Calling update()/value() on a config class with a field declared as Optional<List<String>>, Optional<Map<String,String>>, or any Optional of a generic type, with a non-blank value present in the properties map.","commonSituations":"Adding an Optional-typed convenience field to a Pulsar configuration class with a nested generic; refactoring an existing List/Map field into Optional without realizing FieldParser cannot handle it.","solutions":["Change the field type to the plain generic collection (e.g. List<String> or Map<String,String>) which value() does support.","Keep Optional<T> but make T a simple type (String, Integer, Long, Double, Float, Boolean, or enum).","Handle such a field outside FieldParser: parse it manually with Jackson and set it via a setter instead of relying on update()."],"exampleFix":"// before\nprivate Optional<List<String>> topicPatterns; // unsupported\n// after\nprivate List<String> topicPatterns; // or Optional<String> patternRegex","handlingStrategy":"validation","validationCode":"static void checkOptionalField(Field f) {\n    if (Optional.class.equals(f.getType())) {\n        Type arg = ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0];\n        if (arg instanceof ParameterizedType) {\n            throw new IllegalArgumentException(\"Optional<\" + arg + \"> not supported for field \" + f.getName());\n        }\n    }\n}","typeGuard":"static boolean isSimpleOptional(Field f) {\n    return Optional.class.equals(f.getType())\n            && !(((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0] instanceof ParameterizedType);\n}","tryCatchPattern":"try {\n    FieldParser.update(props, conf);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"unsupported non-primitive Optional\")) {\n        throw new IllegalStateException(\"redefine field as plain or simple Optional type\", e);\n    }\n    throw e;\n}","preventionTips":["Declare config fields as Optional<String|Integer|Boolean|enum> only","Prefer plain List/Map/Set fields over Optional-wrapped collections","Add a startup test iterating all config fields with checkOptionalField","Handle nested-generic fields manually outside FieldParser"],"tags":["reflection","generics","optional","configuration"],"backgroundTag":"unsupported-generic-field-type","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}