grpc/grpc-java · error · IllegalStateException

Cannot disable auto flow control after call started. Use…

Error message

Cannot disable auto flow control after call started. Use ClientResponseObserver

What it means

ClientCalls' ClientCallStreamObserver throws IllegalStateException if disableAutoRequestWithInitial is called after the call is frozen (started). Disabling automatic inbound flow control must be configured before the RPC begins, typically via ClientResponseObserver.beforeStart.

Solutions

  1. Implement the response observer as ClientResponseObserver and call disableAutoRequestWithInitial in beforeStart()
  2. Ensure the wrapper disables auto flow control before passing the observer to async*() methods
  3. Keep request() calls manual from the very start of the stream

Example fix

// before
observer.disableAutoInboundFlowControl(); // called after start -> throws
// after
public void beforeStart(ClientCallStreamObserver<Req> obs) { obs.disableAutoInboundFlowControl(); }
Defensive patterns

Strategy: type-guard

Try / catch

try { observer.disableAutoInboundFlowControl(); } catch (IllegalStateException e) { throw new IllegalStateException("Disable auto flow control in ClientResponseObserver.beforeStart", e); }

Prevention

When it happens

Trigger: Calling disableAutoInboundFlowControl()/disableAutoRequestWithInitial on the stream observer after starting an async call and receiving any observer callback.

Common situations: Setting up manual request() flow control lazily; copying Netty/channel option patterns to the observer API; libraries wrapping observers that configure after first message.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/d8b48fc3dba1f9ac. Report an issue: GitHub.

Appendix: source

Thrown at stub/src/main/java/io/grpc/stub/ClientCalls.java:505

    @Override
    public void setOnReadyHandler(Runnable onReadyHandler) {
      if (frozen) {
        throw new IllegalStateException(
            "Cannot alter onReadyHandler after call started. Use ClientResponseObserver");
      }
      this.onReadyHandler = onReadyHandler;
    }

    @Override
    public void disableAutoInboundFlowControl() {
      disableAutoRequestWithInitial(1);
    }

    @Override
    public void disableAutoRequestWithInitial(int request) {
      if (frozen) {
        throw new IllegalStateException(
            "Cannot disable auto flow control after call started. Use ClientResponseObserver");
      }
      Preconditions.checkArgument(request >= 0, "Initial requests must be non-negative");
      initialRequest = request;
      autoRequestEnabled = false;
    }

    @Override
    public void request(int count) {
      if (!streamingResponse && count == 1) {
        // Initially ask for two responses from flow-control so that if a misbehaving server
        // sends more than one response, we can catch it and fail it in the listener.
        call.request(2);
      } else {
        call.request(count);
      }
    }

View on GitHub (pinned to 64daddc1f3)