{"record":{"id":"c4deb2af95ccf0a7","repo":"apache/pulsar","slug":"fieldname-cannot-be-null","errorCode":null,"errorMessage":"${fieldName} cannot be null","messagePattern":"(.+?) cannot be null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-io/common/src/main/java/org/apache/pulsar/io/common/IOConfigUtils.java","lineNumber":87,"sourceCode":"                        try {\n                            secret = secretsGetter.apply(field.getName());\n                        } catch (Exception e) {\n                            log.warn().attr(\"secret\", field.getName()).exception(e)\n                                    .log(\"Failed to read secret\");\n                            break;\n                        }\n                        if (secret != null) {\n                            configs.put(field.getName(), secret);\n                        }\n                    }\n                    configs.computeIfAbsent(field.getName(), key -> {\n                        // Use default value if it is not null before checking required\n                        String value = fieldDoc.defaultValue();\n                        if (value != null && !value.isEmpty()) {\n                            return value;\n                        }\n                        if (fieldDoc.required()) {\n                            throw new IllegalArgumentException(field.getName() + \" cannot be null\");\n                        }\n                        return null;\n                    });\n                }\n            }\n        }\n        return MAPPER.convertValue(configs, clazz);\n    }\n\n    private static List<Field> getAllFields(Class<?> type) {\n        List<Field> fields = new LinkedList<>();\n        fields.addAll(Arrays.asList(type.getDeclaredFields()));\n        if (type.getSuperclass() != null) {\n            fields.addAll(getAllFields(type.getSuperclass()));\n        }\n        return fields;\n    }\n}","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-io/common/src/main/java/org/apache/pulsar/io/common/IOConfigUtils.java#L69-L105","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add the missing property named exactly as the Java field to the connector config map.","Set a @FieldDoc(defaultValue = ...) in the connector config class if a sensible default exists.","Check that secrets injection worked so required secret-backed fields are not empty.","Validate the config against the connector's Config class before submitting with pulsar-admin."],"exampleFix":"// before (config submitted to pulsar-admin sinks create)\n// {\"topic\": \"persistent://public/default/in\"}  // missing required 'password'\n// after\n// {\"topic\": \"persistent://public/default/in\", \"password\": \"${SECRETPW}\"}\n// or in the config class:\n// @FieldDoc(required = true, defaultValue = \"guest\", help = \"user\")","handlingStrategy":"validation","validationCode":"// Validate required @FieldDoc fields before calling loadWithSecrets\nfor (Field f : TenantSourceConfig.class.getDeclaredFields()) {\n    FieldDoc doc = f.getAnnotation(FieldDoc.class);\n    if (doc != null && doc.required() && configMap.get(f.getName()) == null\n            && (doc.defaultValue() == null || doc.defaultValue().isEmpty())) {\n        throw new IllegalArgumentException(\"Missing required config: \" + f.getName());\n    }\n}","typeGuard":"boolean hasRequired(Map<String, Object> cfg, String key) {\n    Object v = cfg == null ? null : cfg.get(key);\n    return v instanceof String s && !s.isBlank();\n}","tryCatchPattern":"try {\n    MyConfig cfg = IOConfigUtils.loadWithSecrets(configMap, MyConfig.class, secrets);\n} catch (IllegalArgumentException e) {\n    log.error(\"Connector config missing required field: {}\", e.getMessage());\n    throw new ConnectorConfigException(e.getMessage(), e);\n}","preventionTips":["Validate connector config against its Config class before pulsar-admin submit","Use @FieldDoc(required=true) only where a true default doesn't exist","Verify secret injection resolves to non-empty values in the target environment"],"tags":["pulsar-io","configuration","required-field","illegalargument"],"backgroundTag":"missing-config-property","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"}