apache/pulsar · error · IllegalArgumentException

${fieldName} cannot be null

Error message

${fieldName} cannot be null

What it means

IOConfigUtils.loadWithSecrets() reflects over a config class annotated with @FieldDoc to map config values. When a field is marked required=true in @FieldDoc but the provided config map contains no value (and no non-empty defaultValue), it throws IllegalArgumentException('<fieldName> cannot be null'). It is the library's declarative required-config check for Pulsar IO connectors.

Source

Thrown at pulsar-io/common/src/main/java/org/apache/pulsar/io/common/IOConfigUtils.java:87

                        try {
                            secret = secretsGetter.apply(field.getName());
                        } catch (Exception e) {
                            log.warn().attr("secret", field.getName()).exception(e)
                                    .log("Failed to read secret");
                            break;
                        }
                        if (secret != null) {
                            configs.put(field.getName(), secret);
                        }
                    }
                    configs.computeIfAbsent(field.getName(), key -> {
                        // Use default value if it is not null before checking required
                        String value = fieldDoc.defaultValue();
                        if (value != null && !value.isEmpty()) {
                            return value;
                        }
                        if (fieldDoc.required()) {
                            throw new IllegalArgumentException(field.getName() + " cannot be null");
                        }
                        return null;
                    });
                }
            }
        }
        return MAPPER.convertValue(configs, clazz);
    }

    private static List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new LinkedList<>();
        fields.addAll(Arrays.asList(type.getDeclaredFields()));
        if (type.getSuperclass() != null) {
            fields.addAll(getAllFields(type.getSuperclass()));
        }
        return fields;
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Add the missing property named exactly as the Java field to the connector config map.
  2. Set a @FieldDoc(defaultValue = ...) in the connector config class if a sensible default exists.
  3. Check that secrets injection worked so required secret-backed fields are not empty.
  4. Validate the config against the connector's Config class before submitting with pulsar-admin.

Example fix

// before (config submitted to pulsar-admin sinks create)
// {"topic": "persistent://public/default/in"}  // missing required 'password'
// after
// {"topic": "persistent://public/default/in", "password": "${SECRETPW}"}
// or in the config class:
// @FieldDoc(required = true, defaultValue = "guest", help = "user")
Defensive patterns

Strategy: validation

Validate before calling

// Validate required @FieldDoc fields before calling loadWithSecrets
for (Field f : TenantSourceConfig.class.getDeclaredFields()) {
    FieldDoc doc = f.getAnnotation(FieldDoc.class);
    if (doc != null && doc.required() && configMap.get(f.getName()) == null
            && (doc.defaultValue() == null || doc.defaultValue().isEmpty())) {
        throw new IllegalArgumentException("Missing required config: " + f.getName());
    }
}

Type guard

boolean hasRequired(Map<String, Object> cfg, String key) {
    Object v = cfg == null ? null : cfg.get(key);
    return v instanceof String s && !s.isBlank();
}

Try / catch

try {
    MyConfig cfg = IOConfigUtils.loadWithSecrets(configMap, MyConfig.class, secrets);
} catch (IllegalArgumentException e) {
    log.error("Connector config missing required field: {}", e.getMessage());
    throw new ConnectorConfigException(e.getMessage(), e);
}

Prevention

When it happens

Trigger: Calling IOConfigUtils.loadWithSecrets(configMap, ConfigClass.class, secrets) with a map missing a field whose @FieldDoc(required=true) is set, or providing only an empty default value and no actual value.

Common situations: Submitting a Pulsar IO source/sink whose JSON/YAML config omits mandatory fields (e.g. missing password/requiredParam); environment variable or secret injection failing so the key resolves to nothing; renaming a field in config but not in the Java config class.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/c4deb2af95ccf0a7. Report an issue: GitHub.