apache/beam · error · IllegalStateException
RateLimitServiceStub is null
Error message
RateLimitServiceStub is null
What it means
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.
Source
Thrown at sdks/java/io/components/src/main/java/org/apache/beam/sdk/io/components/ratelimiter/EnvoyRateLimiterFactory.java:136
return true;
}
if (!(context instanceof EnvoyRateLimiterContext)) {
throw new IllegalArgumentException(
"EnvoyRateLimiterFactory requires EnvoyRateLimiterContext, got: "
+ context.getClass().getName());
}
checkArgument(permits >= 0, "Permits must be non-negative");
EnvoyRateLimiterContext envoyContext = (EnvoyRateLimiterContext) context;
return fetchTokens(envoyContext, permits);
}
private boolean fetchTokens(EnvoyRateLimiterContext context, int tokens)
throws IOException, InterruptedException {
init();
RateLimitServiceGrpc.RateLimitServiceBlockingStub currentStub = stub;
if (currentStub == null) {
throw new IllegalStateException("RateLimitServiceStub is null");
}
Map<String, String> descriptors = context.getDescriptors();
RateLimitDescriptor.Builder descriptorBuilder = RateLimitDescriptor.newBuilder();
for (Map.Entry<String, String> entry : descriptors.entrySet()) {
descriptorBuilder.addEntries(
RateLimitDescriptor.Entry.newBuilder()
.setKey(entry.getKey())
.setValue(entry.getValue())
.build());
}
RateLimitRequest request =
RateLimitRequest.newBuilder()
.setDomain(context.getDomain())
.setHitsAddend(tokens)
.addDescriptors(descriptorBuilder.build())View on GitHub (pinned to 12126d8942)
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.
Example fix
// before EnvoyRateLimiterFactory factory = new EnvoyRateLimiterFactory(config, null); // stub never set // after ManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); RateLimitServiceGrpc.RateLimitServiceBlockingStub stub = RateLimitServiceGrpc.newBlockingStub(channel); EnvoyRateLimiterFactory factory = new EnvoyRateLimiterFactory(config, stub);
Defensive patterns
Strategy: validation
Validate before calling
if (stub == null) {
throw new IllegalStateException("initialize EnvoyRateLimiterFactory with a non-null RateLimitService stub before allow()");
} Try / catch
try {
allowed = factory.allow(ctx, permits);
} catch (IllegalStateException e) {
// re-init the factory/channel, then retry once
factory.init();
allowed = factory.allow(ctx, permits);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- EnvoyRateLimiterFactory requires EnvoyRateLimiterContext
- EnvoyRateLimiterFactory requires EnvoyRateLimiterContext, go
- Failed to call Rate Limit Service
- Failed to get response from Rate Limit Service
- Rate Limit Service returned unknown code:
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/6ee0a32a6ef3649f.
Report an issue: GitHub.