{"record":{"id":"1507e84cb5ff8794","repo":"grpc/grpc-java","slug":"channellogger-is-not-set-in-builder","errorCode":null,"errorMessage":"ChannelLogger is not set in Builder","messagePattern":"ChannelLogger is not set in Builder","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/io/grpc/NameResolver.java","lineNumber":471,"sourceCode":"     *\n     * <p>Custom args can also be used simply to avoid adding inappropriate deps to the low level\n     * io.grpc package.\n     */\n    @SuppressWarnings(\"unchecked\") // Cast is safe because all put()s go through the setArg() API.\n    @Nullable\n    public <T> T getArg(Key<T> key) {\n      return customArgs != null ? (T) customArgs.get(key) : null;\n    }\n\n    /**\n     * Returns the {@link ChannelLogger} for the Channel served by this NameResolver.\n     *\n     * @since 1.26.0\n     */\n    @ExperimentalApi(\"https://github.com/grpc/grpc-java/issues/6438\")\n    public ChannelLogger getChannelLogger() {\n      if (channelLogger == null) {\n        throw new IllegalStateException(\"ChannelLogger is not set in Builder\");\n      }\n      return channelLogger;\n    }\n\n    /**\n     * Returns the configurator for child channels.\n     *\n     * @since 1.83.0\n     */\n    @ExperimentalApi(\"https://github.com/grpc/grpc-java/issues/12574\")\n    public ChannelConfigurator getChildChannelConfigurator() {\n      return channelConfigurator;\n    }\n\n    /**\n     * Returns the Executor on which this resolver should execute long-running or I/O bound work.\n     * Null if no Executor was set.\n     *","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/api/src/main/java/io/grpc/NameResolver.java#L453-L489","documentation":"NameResolver.Args.Builder.getChannelLogger() throws IllegalStateException when channelLogger was never set via setChannelLogger(). The ChannelLogger is an experimental API component (since 1.26.0) that NameResolver implementations use to emit channel-level trace logs. gRPC requires it to be provided before the Args object is consumed, so reading it in an unset state is treated as a programming/invariant error rather than a recoverable failure.","triggerScenarios":"Calling getChannelLogger() on a NameResolver.Args instance built by a Builder on which setChannelLogger() was never called — typically a custom NameResolver implementation reading Args inside its factory (newNameResolver) or when the Args were constructed by non-gRPC code that omitted the field.","commonSituations":"Implementing a custom NameResolver/NameResolverProvider and calling args.getChannelLogger() while testing with a hand-built Args.Builder that only sets the fields you know about; upgrading gRPC where a previously optional Args field became required for your code path; unit tests that construct NameResolver.Args directly instead of receiving them from the channel.","solutions":["Call setChannelLogger(...) on the Args.Builder before building, e.g. with args.getChannelLogger() passed in from the channel, or a Noop/Testing logger in tests","Guard the call: use args.getChannelLogger() only inside a NameResolver whose Args were supplied by grpc's ManagedChannel (which always sets it); for hand-built Args in tests, substitute a simple ChannelLogger implementation","If the Args come from your own provider code, audit the builder call chain and add the missing setChannelLogger call"],"exampleFix":"// before\nNameResolver.Args args = new NameResolver.Args.Builder()\n    .setUri(uri)\n    .setAttributes(attributes)\n    .build();\nChannelLogger logger = args.getChannelLogger(); // throws IllegalStateException\n\n// after\nNameResolver.Args args = new NameResolver.Args.Builder()\n    .setUri(uri)\n    .setAttributes(attributes)\n    .setChannelLogger(channelLogger)\n    .build();\nChannelLogger logger = args.getChannelLogger();","handlingStrategy":"validation","validationCode":"// before consuming Args\nif (args.getChannelLoggerOrNull() == null) { // or track your own builder flag\n  throw new IllegalArgumentException(\"NameResolver.Args built without setChannelLogger\");\n}\nChannelLogger logger = args.getChannelLogger();","typeGuard":"boolean hasChannelLogger(NameResolver.Args args) {\n  try { return args.getChannelLogger() != null; }\n  catch (IllegalStateException e) { return false; }\n}","tryCatchPattern":"try {\n  logger = args.getChannelLogger();\n} catch (IllegalStateException e) {\n  logger = new NoopChannelLogger(); // fall back in tests/custom builders\n}","preventionTips":["Always build NameResolver.Args through the full setX chain including setChannelLogger","In unit tests, use a Noop/Recording ChannelLogger implementation instead of leaving the field null","When upgrading gRPC, review NameResolver.Args.Builder for newly added required fields"],"tags":["grpc","java","illegal-state","name-resolver","builder"],"backgroundTag":"missing-required-config-field","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}