{"id":"29128b0b2c3c704e","repo":"apache/kafka","slug":"mode-must-be-non-null-if-securityprotocol-is","errorCode":null,"errorMessage":"`mode` must be non-null if `securityProtocol` is `${securityProtocol}`","messagePattern":"`mode` must be non-null if `securityProtocol` is `(.+?)`","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/network/ChannelBuilders.java","lineNumber":215,"sourceCode":"        if (listenerName == null)\n            parsedConfigs = (Map<String, Object>) config.values();\n        else\n            parsedConfigs = config.valuesWithPrefixOverride(listenerName.configPrefix());\n\n        config.originals().entrySet().stream()\n            .filter(e -> !parsedConfigs.containsKey(e.getKey())) // exclude already parsed configs\n            // exclude already parsed listener prefix configs\n            .filter(e -> !(listenerName != null && e.getKey().startsWith(listenerName.configPrefix()) &&\n                parsedConfigs.containsKey(e.getKey().substring(listenerName.configPrefix().length()))))\n            // exclude keys like `{mechanism}.some.prop` if \"listener.name.\" prefix is present and key `some.prop` exists in parsed configs.\n            .filter(e -> !(listenerName != null && parsedConfigs.containsKey(e.getKey().substring(e.getKey().indexOf('.') + 1))))\n            .forEach(e -> parsedConfigs.put(e.getKey(), e.getValue()));\n        return parsedConfigs;\n    }\n\n    private static void requireNonNullMode(ConnectionMode connectionMode, SecurityProtocol securityProtocol) {\n        if (connectionMode == null)\n            throw new IllegalArgumentException(\"`mode` must be non-null if `securityProtocol` is `\" + securityProtocol + \"`\");\n    }\n\n    public static KafkaPrincipalBuilder createPrincipalBuilder(Map<String, ?> configs,\n                                                               KerberosShortNamer kerberosShortNamer,\n                                                               SslPrincipalMapper sslPrincipalMapper) {\n        Class<?> principalBuilderClass = (Class<?>) configs.get(BrokerSecurityConfigs.PRINCIPAL_BUILDER_CLASS_CONFIG);\n        final KafkaPrincipalBuilder builder;\n\n        if (principalBuilderClass == null || principalBuilderClass == DefaultKafkaPrincipalBuilder.class) {\n            builder = new DefaultKafkaPrincipalBuilder(kerberosShortNamer, sslPrincipalMapper);\n        } else if (KafkaPrincipalBuilder.class.isAssignableFrom(principalBuilderClass)) {\n            builder = (KafkaPrincipalBuilder) Utils.newInstance(principalBuilderClass);\n        } else {\n            throw new InvalidConfigurationException(\"Type \" + principalBuilderClass.getName() + \" is not \" +\n                    \"an instance of \" + KafkaPrincipalBuilder.class.getName());\n        }\n\n        if (builder instanceof Configurable)","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/network/ChannelBuilders.java#L197-L233","documentation":"Thrown by `ChannelBuilders.requireNonNullMode` (invoked from the internal `create(...)` method) when `connectionMode` is null for SSL, SASL_SSL, or SASL_PLAINTEXT protocols. Those protocols are directional — they build different `SslChannelBuilder`/`SaslChannelBuilder` instances for CLIENT vs SERVER, including different JAAS context loading and ssl.client.auth override behavior — so a null mode has no sensible default. PLAINTEXT does not require a mode and skips the check.","triggerScenarios":"Reaching `create(...)` for SSL/SASL_SSL/SASL_PLAINTEXT with `connectionMode == null`. The public `clientChannelBuilder` always passes `ConnectionMode.CLIENT` and `serverChannelBuilder` passes `ConnectionMode.SERVER`, so this is normally only hit by code that calls the package/private `create` method directly or by a future refactor that introduces a null mode path.","commonSituations":"Custom broker/client tooling that invokes the internal create() path with a null ConnectionMode. Test harnesses that construct ChannelBuilders reflectively. A bug in a fork or downstream distributor that bypasses the public client/server entry points.","solutions":["Use the public `clientChannelBuilder(...)` or `serverChannelBuilder(...)` entry points — they always supply a non-null ConnectionMode.","If you must call `create(...)` directly, pass `ConnectionMode.CLIENT` or `ConnectionMode.SERVER` explicitly.","Add an assertion in your wrapper so a null mode fails earlier with a clearer message."],"exampleFix":"// before (internal call)\nChannelBuilders.create(SecurityProtocol.SSL, null, contextType,\n    config, listenerName, false, null, null, null, time, logContext, null);\n\n// after\nChannelBuilders.clientChannelBuilder(SecurityProtocol.SSL,\n    contextType, config, listenerName, clientSaslMechanism, time, logContext);","handlingStrategy":"try-catch","validationCode":"// `mode` (ConnectionMode) is not a parameter of the public clientChannelBuilder/serverChannelBuilder\n// factories; it is supplied internally. The only user-side check is to avoid reflection/private-API use:\nif (securityProtocol == SecurityProtocol.SSL\n        || securityProtocol == SecurityProtocol.SASL_SSL\n        || securityProtocol == SecurityProtocol.SASL_PLAINTEXT) {\n    // ensure you are using ChannelBuilders.clientChannelBuilder(...) or serverChannelBuilder(...),\n    // both of which pass a non-null ConnectionMode. Do not call the private create(...) directly.\n}","typeGuard":null,"tryCatchPattern":"try {\n    ChannelBuilders.clientChannelBuilder(\n        securityProtocol, contextType, config, listenerName,\n        clientSaslMechanism, time, logContext);\n} catch (IllegalArgumentException e) {\n    // \"`mode` must be non-null if `securityProtocol` is ...\" -- indicates non-public API misuse.\n    throw new IllegalStateException(\"ChannelBuilder produced without a connection mode\", e);\n}","preventionTips":["Use only the public clientChannelBuilder/serverChannelBuilder factories; they always set a non-null ConnectionMode.","Do not invoke ChannelBuilders.create(...) via reflection -- it is private and skips mode setup.","If this fires, suspect a forked/patched Kafka build or reflective wiring, not normal configuration."],"tags":["network","ssl","sasl","security","internal","validation","java"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}