{"record":{"id":"7ad6c2b51d9a3a1f","repo":"grpc/grpc-java","slug":"subclass-failed-to-hide-static-factory","errorCode":null,"errorMessage":"Subclass failed to hide static factory","messagePattern":"Subclass failed to hide static factory","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/io/grpc/ForwardingChannelBuilder2.java","lineNumber":51,"sourceCode":" *\n * @param <T> The type of the subclass extending this abstract class.\n * @since 1.59.0\n */\npublic abstract class ForwardingChannelBuilder2<T extends ManagedChannelBuilder<T>>\n    extends ManagedChannelBuilder<T> {\n\n  /**\n   * The default constructor.\n   */\n  protected ForwardingChannelBuilder2() {\n  }\n\n  /**\n   * This method serves to force subclasses to \"hide\" this static factory.\n   */\n  @DoNotCall(\"Unsupported\")\n  public static ManagedChannelBuilder<?> forAddress(String name, int port) {\n    throw new UnsupportedOperationException(\"Subclass failed to hide static factory\");\n  }\n\n  /**\n   * This method serves to force subclasses to \"hide\" this static factory.\n   */\n  @DoNotCall(\"Unsupported\")\n  public static ManagedChannelBuilder<?> forTarget(String target) {\n    throw new UnsupportedOperationException(\"Subclass failed to hide static factory\");\n  }\n\n  /**\n   * Returns the delegated {@code ManagedChannelBuilder}.\n   */\n  protected abstract ManagedChannelBuilder<?> delegate();\n\n  @Override\n  public T directExecutor() {\n    delegate().directExecutor();","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/api/src/main/java/io/grpc/ForwardingChannelBuilder2.java#L33-L69","documentation":"ForwardingChannelBuilder2 is an abstract delegating wrapper around ManagedChannelBuilder. Its static forAddress() factory is marked @DoNotCall and exists only to force subclasses to define their own static forAddress(); invoking the inherited/base one throws UnsupportedOperationException because the wrapper cannot know which concrete builder to construct.","triggerScenarios":"Calling ForwardingChannelBuilder2.forAddress(host, port) directly instead of the subclass's static factory, or calling it through a subclass that failed to shadow (hide) the static method with its own forAddress implementation. Static methods resolve at compile time on the named class, so referencing the base method always hits this throw.","commonSituations":"Reflection or generic code that resolves the static factory on ForwardingChannelBuilder2.class rather than the concrete subclass; a custom forwarding builder that forgot to declare its own forAddress; IDE auto-import picking the wrong class.","solutions":["Call the concrete builder's static factory, e.g. MyForwardingBuilder.forAddress(name, port), never ForwardingChannelBuilder2.forAddress","If you maintain a forwarding subclass, add your own static forAddress(String, int) that delegates to your delegate() implementation","If resolving reflectively, look up the method on the actual subclass Class, not the base class"],"exampleFix":"// before\nManagedChannelBuilder<?> b = ForwardingChannelBuilder2.forAddress(\"localhost\", 8080);\n// after\nManagedChannelBuilder<?> b = MyGrpcChannelBuilder.forAddress(\"localhost\", 8080); // subclass's own static factory","handlingStrategy":"validation","validationCode":"// Never reference the base static factory; verify you are calling the subclass's own\nClass<?> concrete = MyGrpcChannelBuilder.class;\nif (concrete.getMethod(\"forAddress\", String.class, int.class).getDeclaringClass()\n    == io.grpc.ForwardingChannelBuilder2.class) {\n  throw new IllegalStateException(\"Subclass must shadow forAddress\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  builder = MyGrpcChannelBuilder.forAddress(name, port);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"Subclass failed to hide static factory\")) {\n    builder = ManagedChannelBuilder.forAddress(name, port); // fall back to standard builder\n  } else throw e;\n}","preventionTips":["Always invoke static factories on the concrete builder class, never on ForwardingChannelBuilder2","When writing a forwarding builder, immediately add static forAddress/forTarget that mirror the base API","Avoid reflective lookups of forAddress against the base class"],"tags":["unsupported-operation","static-factory","forwarding-builder","api-misuse"],"backgroundTag":"unsupported-operation","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}