{"id":"bbddb220f8535b5f","repo":"apache/kafka","slug":"you-must-set-either-bootstrap-servers-or-bootstrap","errorCode":null,"errorMessage":"You must set either bootstrap.servers or bootstrap.controllers","messagePattern":"You must set either bootstrap\\.servers or bootstrap\\.controllers","errorType":"validation","errorClass":"ConfigException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java","lineNumber":541,"sourceCode":"     * Validates that exactly one of bootstrap.servers or bootstrap.controllers is configured.\n     *\n     * @param config The admin client configuration\n     * @return true if using bootstrap.controllers, false if using bootstrap.servers\n     * @throws ConfigException if both or neither bootstrap configurations are set\n     */\n    static boolean determineBootstrapType(AdminClientConfig config) {\n        List<String> bootstrapServers = config.getList(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG);\n        if (bootstrapServers == null) {\n            bootstrapServers = Collections.emptyList();\n        }\n        List<String> controllerServers = config.getList(AdminClientConfig.BOOTSTRAP_CONTROLLERS_CONFIG);\n        if (controllerServers == null) {\n            controllerServers = Collections.emptyList();\n        }\n\n        if (bootstrapServers.isEmpty()) {\n            if (controllerServers.isEmpty()) {\n                throw new ConfigException(\"You must set either \" +\n                    CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG + \" or \" +\n                    AdminClientConfig.BOOTSTRAP_CONTROLLERS_CONFIG);\n            } else {\n                return true; // Using bootstrap.controllers\n            }\n        } else {\n            if (controllerServers.isEmpty()) {\n                return false; // Using bootstrap.servers\n            } else {\n                throw new ConfigException(\"You cannot set both \" +\n                    CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG + \" and \" +\n                    AdminClientConfig.BOOTSTRAP_CONTROLLERS_CONFIG);\n            }\n        }\n    }\n\n    static KafkaAdminClient createInternal(AdminClientConfig config, TimeoutProcessorFactory timeoutProcessorFactory) {\n        return createInternal(config, timeoutProcessorFactory, null);","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java#L523-L559","documentation":"Thrown by KafkaAdminClient.determineBootstrapType during AdminClient creation when neither bootstrap.servers nor bootstrap.controllers is present in the configuration. The admin client requires exactly one bootstrap source so the NetworkClient can resolve an initial node and seed metadata; with both lists empty there is no entry point to the cluster, so construction fails fast with a ConfigException rather than silently producing a non-functional client.","triggerScenarios":"Calling Admin.create(Properties)/Admin.create(Map) with a config map that omits both CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG (\"bootstrap.servers\") and AdminClientConfig.BOOTSTRAP_CONTROLLERS_CONFIG (\"bootstrap.controllers\"); or passing an empty string for bootstrap.servers (which config.getList parses into an empty list).","commonSituations":"Config loaded from a properties file where the bootstrap.servers line was commented out, mistyped (e.g. \"bootstrap.serverss\"), or overridden to empty by an environment variable; Spring/Quarkus injection that left the property unresolved (literal ${kafka.bootstrap.servers}); code that builds the config map dynamically and the source list came back null.","solutions":["Add bootstrap.servers=<host:port,...> to the Admin config map/properties (the legacy, universally-supported option).","If targeting a KRaft cluster directly, set bootstrap.controllers=<controllerHost:port,...> instead.","Verify the property key spelling is exactly bootstrap.servers (or bootstrap.controllers) and that the value resolves to a non-empty string before calling Admin.create.","Log the resolved config right before Admin.create so a missing/mistyped key is visible at startup."],"exampleFix":"// before\nProperties p = new Properties();\np.put(AdminClientConfig.CLIENT_ID_CONFIG, \"myAdmin\");\nAdmin admin = Admin.create(p); // throws ConfigException\n\n// after\nProperties p = new Properties();\np.put(AdminClientConfig.CLIENT_ID_CONFIG, \"myAdmin\");\np.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, \"broker1:9092,broker2:9092\");\nAdmin admin = Admin.create(p);","handlingStrategy":"validation","validationCode":"boolean hasServers = props.containsKey(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG)\n    && !String.valueOf(props.get(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG)).isBlank();\nboolean hasControllers = props.containsKey(AdminClientConfig.BOOTSTRAP_CONTROLLERS_CONFIG)\n    && !String.valueOf(props.get(AdminClientConfig.BOOTSTRAP_CONTROLLERS_CONFIG)).isBlank();\nif (!hasServers && !hasControllers) {\n    throw new IllegalArgumentException(\"Set exactly one of bootstrap.servers or bootstrap.controllers\");\n}\nAdmin admin = Admin.create(props);","typeGuard":null,"tryCatchPattern":"try {\n    Admin admin = Admin.create(props);\n} catch (ConfigException e) {\n    // 'You must set either bootstrap.servers or bootstrap.controllers'\n    log.error(\"Missing bootstrap config\", e);\n}","preventionTips":["Centralize broker/controller endpoint resolution in a single config builder so every Admin creation site sets exactly one bootstrap property.","Fail fast at application startup with a config validation step rather than letting Admin.create throw.","Prefer bootstrap.servers for broker-facing admin tasks; use bootstrap.controllers only when talking directly to KRaft controllers."],"tags":["configuration","admin-client","bootstrap","startup"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}