{"record":{"id":"6ee0a32a6ef3649f","repo":"apache/beam","slug":"ratelimitservicestub-is-null","errorCode":null,"errorMessage":"RateLimitServiceStub is null","messagePattern":"RateLimitServiceStub is null","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/components/src/main/java/org/apache/beam/sdk/io/components/ratelimiter/EnvoyRateLimiterFactory.java","lineNumber":136,"sourceCode":"      return true;\n    }\n    if (!(context instanceof EnvoyRateLimiterContext)) {\n      throw new IllegalArgumentException(\n          \"EnvoyRateLimiterFactory requires EnvoyRateLimiterContext, got: \"\n              + context.getClass().getName());\n    }\n    checkArgument(permits >= 0, \"Permits must be non-negative\");\n    EnvoyRateLimiterContext envoyContext = (EnvoyRateLimiterContext) context;\n    return fetchTokens(envoyContext, permits);\n  }\n\n  private boolean fetchTokens(EnvoyRateLimiterContext context, int tokens)\n      throws IOException, InterruptedException {\n\n    init();\n    RateLimitServiceGrpc.RateLimitServiceBlockingStub currentStub = stub;\n    if (currentStub == null) {\n      throw new IllegalStateException(\"RateLimitServiceStub is null\");\n    }\n\n    Map<String, String> descriptors = context.getDescriptors();\n    RateLimitDescriptor.Builder descriptorBuilder = RateLimitDescriptor.newBuilder();\n\n    for (Map.Entry<String, String> entry : descriptors.entrySet()) {\n      descriptorBuilder.addEntries(\n          RateLimitDescriptor.Entry.newBuilder()\n              .setKey(entry.getKey())\n              .setValue(entry.getValue())\n              .build());\n    }\n\n    RateLimitRequest request =\n        RateLimitRequest.newBuilder()\n            .setDomain(context.getDomain())\n            .setHitsAddend(tokens)\n            .addDescriptors(descriptorBuilder.build())","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/components/src/main/java/org/apache/beam/sdk/io/components/ratelimiter/EnvoyRateLimiterFactory.java#L118-L154","documentation":"Inside EnvoyRateLimiterFactory.fetchTokens(), after init() the gRPC RateLimitServiceBlockingStub field is read into a local and checked for null; if the channel was never established (or init failed silently), an IllegalStateException(\"RateLimitServiceStub is null\") is thrown rather than sending a request on a null stub.","triggerScenarios":"Calling allow() before the factory's asynchronous init() completed, init() failed to create the channel/stub (e.g. bad target address, TLS setup failure), or the stub field was never set because the factory was constructed without a stub.","commonSituations":"Envoy rate-limit service unreachable or hostname misconfigured at pipeline startup, race between construction and first allow() call in a multithreaded DoFn setup, or missing gRPC dependencies preventing channel creation.","solutions":["Ensure the Envoy rate-limit service address is reachable and that init() runs (e.g. in DoFn.setup()) before any allow() call.","Fix the constructor wiring so a valid RateLimitServiceGrpc stub/channel is passed to the factory.","Retry after init — if init() is lazy/async, make it synchronous or wait for completion before fetching tokens."],"exampleFix":"// before\nEnvoyRateLimiterFactory factory = new EnvoyRateLimiterFactory(config, null); // stub never set\n// after\nManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build();\nRateLimitServiceGrpc.RateLimitServiceBlockingStub stub = RateLimitServiceGrpc.newBlockingStub(channel);\nEnvoyRateLimiterFactory factory = new EnvoyRateLimiterFactory(config, stub);","handlingStrategy":"validation","validationCode":"if (stub == null) {\n  throw new IllegalStateException(\"initialize EnvoyRateLimiterFactory with a non-null RateLimitService stub before allow()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  allowed = factory.allow(ctx, permits);\n} catch (IllegalStateException e) {\n  // re-init the factory/channel, then retry once\n  factory.init();\n  allowed = factory.allow(ctx, permits);\n}","preventionTips":["Call any init/setup (e.g. DoFn.setup()) before the first allow().","Verify the rate-limit service target resolves at pipeline startup.","Ensure the gRPC channel/stub is constructed eagerly in the factory constructor."],"tags":["grpc","rate-limiting","envoy","null-check","initialization"],"backgroundTag":"grpc-unavailable","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}