{"record":{"id":"a8c09f7e853c403f","repo":"grpc/grpc-java","slug":"scheduledexecutorservice-not-set-in-builder","errorCode":null,"errorMessage":"ScheduledExecutorService not set in Builder","messagePattern":"ScheduledExecutorService not set in Builder","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/io/grpc/NameResolver.java","lineNumber":425,"sourceCode":"    public SynchronizationContext getSynchronizationContext() {\n      return syncContext;\n    }\n\n    /**\n     * Returns a {@link ScheduledExecutorService} for scheduling delayed tasks.\n     *\n     * <p>This service is a shared resource and is only meant for quick tasks. DO NOT block or run\n     * time-consuming tasks.\n     *\n     * <p>The returned service doesn't support {@link ScheduledExecutorService#shutdown shutdown()}\n     *  and {@link ScheduledExecutorService#shutdownNow shutdownNow()}. They will throw if called.\n     *\n     * @since 1.26.0\n     */\n    @ExperimentalApi(\"https://github.com/grpc/grpc-java/issues/6454\")\n    public ScheduledExecutorService getScheduledExecutorService() {\n      if (scheduledExecutorService == null) {\n        throw new IllegalStateException(\"ScheduledExecutorService not set in Builder\");\n      }\n      return scheduledExecutorService;\n    }\n\n    /**\n     * Returns the {@link ServiceConfigParser}.\n     *\n     * @since 1.21.0\n     */\n    public ServiceConfigParser getServiceConfigParser() {\n      return serviceConfigParser;\n    }\n\n    /**\n     * Returns the value of a custom arg named 'key', or {@code null} if it's not set.\n     *\n     * <p>While ordinary {@link Args} should be universally useful and meaningful, custom arguments\n     * can apply just to resolvers of a certain URI scheme, just to resolvers producing a particular","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/api/src/main/java/io/grpc/NameResolver.java#L407-L443","documentation":"NameResolver.Args.getScheduledExecutorService throws IllegalStateException when no ScheduledExecutorService was provided to the Args.Builder before this getter is called. The field is optional at build time but required at read time, so the accessor enforces the invariant. It signals that the caller (typically a NameResolver implementation or a NameResolverProvider wiring path) expects an executor the builder never set.","triggerScenarios":"Calling getScheduledExecutorService() on NameResolver.Args built without builder.scheduledExecutorService(...). Happens in custom NameResolver/NameResolverProvider implementations that read the executor, or when older provider/wrapper code (wrap, newNameResolver) builds Args without the executor.","commonSituations":"Upgrading grpc-java: NameResolverProvider APIs changed and custom providers construct Args without setting the executor; copying a NameResolver.Args builder but omitting scheduledExecutorService; a library's NameResolver assuming the executor exists on older args instances.","solutions":["Call .scheduledExecutorService(executor) on the Args.Builder before building — e.g. pass the channel's or a shared ScheduledThreadPoolExecutor.","When implementing a NameResolver, avoid depending on the executor if args may lack it, or check availability first and provide a default executor.","If you control provider code, update it to copy all fields from existing Args (including scheduledExecutorService) when rebuilding/wrapping args.","Verify grpc-java version alignment: NameResolverProvider signatures changed in 1.26+; rebuild custom providers against the current API."],"exampleFix":"// before\nNameResolver.Args args = NameResolver.Args.newBuilder()\n    .setDefaultPort(443)\n    .setServiceConfigParser(parser)\n    .build();\n// after\nNameResolver.Args args = NameResolver.Args.newBuilder()\n    .setDefaultPort(443)\n    .setServiceConfigParser(parser)\n    .setScheduledExecutorService(sharedScheduler)\n    .build();","handlingStrategy":"try-catch","validationCode":"NameResolver.Args.Builder b = NameResolver.Args.newBuilder()...; if (b != null) { /* ensure setScheduledExecutorService was called before build() */ }","typeGuard":"boolean hasScheduler(NameResolver.Args args) { try { return args.getScheduledExecutorService() != null; } catch (IllegalStateException e) { return false; } }","tryCatchPattern":"ScheduledExecutorService ses; try { ses = args.getScheduledExecutorService(); } catch (IllegalStateException e) { ses = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r, \"name-resolver\"); t.setDaemon(true); return t; }); }","preventionTips":["Always call setScheduledExecutorService on Args.Builder before build()","When wrapping/copying Args, copy every field including the executor","Rebuild custom NameResolverProvider against the current grpc-java API","Prefer supplying a shared daemon scheduler rather than relying on defaults"],"tags":["grpc","name-resolution","missing-configuration","illegal-state"],"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"}