apache/pulsar · error · IllegalArgumentException
unsupported non-primitive Optional<%s> for %s
Error message
unsupported non-primitive Optional<%s> for %s
What it means
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.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/FieldParser.java:191
requireNonNull(field);
// if field is not primitive type
Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType) {
Class<?> clazz = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
if (field.getType().equals(List.class)) {
// convert to list
return stringToList(strValue, clazz);
} else if (field.getType().equals(Set.class)) {
// covert to set
return stringToSet(strValue, clazz);
} else if (field.getType().equals(Map.class)) {
Class<?> valueClass =
(Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1];
return stringToMap(strValue, clazz, valueClass);
} else if (field.getType().equals(Optional.class)) {
Type typeClazz = ((ParameterizedType) fieldType).getActualTypeArguments()[0];
if (typeClazz instanceof ParameterizedType) {
throw new IllegalArgumentException(format("unsupported non-primitive Optional<%s> for %s",
typeClazz.getClass(), field.getName()));
}
@SuppressWarnings("unchecked") // typeClazz is verified to be a non-parameterized Class
Optional<?> result = Optional.ofNullable(convert(strValue, (Class<?>) typeClazz));
return result;
} else {
throw new IllegalArgumentException(
format("unsupported field-type %s for %s", field.getType(), field.getName()));
}
} else {
return convert(strValue, field.getType());
}
}
/**
* Sets the empty/null value if field is allowed to be set empty.
*
* @param strValueView on GitHub (pinned to 820761864e)
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().
Example fix
// before private Optional<List<String>> topicPatterns; // unsupported // after private List<String> topicPatterns; // or Optional<String> patternRegex
Defensive patterns
Strategy: validation
Validate before calling
static void checkOptionalField(Field f) {
if (Optional.class.equals(f.getType())) {
Type arg = ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0];
if (arg instanceof ParameterizedType) {
throw new IllegalArgumentException("Optional<" + arg + "> not supported for field " + f.getName());
}
}
} Type guard
static boolean isSimpleOptional(Field f) {
return Optional.class.equals(f.getType())
&& !(((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0] instanceof ParameterizedType);
} Try / catch
try {
FieldParser.update(props, conf);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("unsupported non-primitive Optional")) {
throw new IllegalStateException("redefine field as plain or simple Optional type", e);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- unsupported field-type %s for %s
- Failed to instantiate ${className}
- Exception caused while converting configuration: ${message}
- Failed to compute configuration overrides
- Failed to create authentication: ${message}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/9c1008f5b43035e0.
Report an issue: GitHub.