{"record":{"id":"d8b48fc3dba1f9ac","repo":"grpc/grpc-java","slug":"cannot-disable-auto-flow-control-after-call-starte","errorCode":null,"errorMessage":"Cannot disable auto flow control after call started. Use ClientResponseObserver","messagePattern":"Cannot disable auto flow control after call started\\. Use ClientResponseObserver","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"stub/src/main/java/io/grpc/stub/ClientCalls.java","lineNumber":505,"sourceCode":"\n    @Override\n    public void setOnReadyHandler(Runnable onReadyHandler) {\n      if (frozen) {\n        throw new IllegalStateException(\n            \"Cannot alter onReadyHandler after call started. Use ClientResponseObserver\");\n      }\n      this.onReadyHandler = onReadyHandler;\n    }\n\n    @Override\n    public void disableAutoInboundFlowControl() {\n      disableAutoRequestWithInitial(1);\n    }\n\n    @Override\n    public void disableAutoRequestWithInitial(int request) {\n      if (frozen) {\n        throw new IllegalStateException(\n            \"Cannot disable auto flow control after call started. Use ClientResponseObserver\");\n      }\n      Preconditions.checkArgument(request >= 0, \"Initial requests must be non-negative\");\n      initialRequest = request;\n      autoRequestEnabled = false;\n    }\n\n    @Override\n    public void request(int count) {\n      if (!streamingResponse && count == 1) {\n        // Initially ask for two responses from flow-control so that if a misbehaving server\n        // sends more than one response, we can catch it and fail it in the listener.\n        call.request(2);\n      } else {\n        call.request(count);\n      }\n    }\n","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/stub/src/main/java/io/grpc/stub/ClientCalls.java#L487-L523","documentation":"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.","triggerScenarios":"Calling disableAutoInboundFlowControl()/disableAutoRequestWithInitial on the stream observer after starting an async call and receiving any observer callback.","commonSituations":"Setting up manual request() flow control lazily; copying Netty/channel option patterns to the observer API; libraries wrapping observers that configure after first message.","solutions":["Implement the response observer as ClientResponseObserver and call disableAutoRequestWithInitial in beforeStart()","Ensure the wrapper disables auto flow control before passing the observer to async*() methods","Keep request() calls manual from the very start of the stream"],"exampleFix":"// before\nobserver.disableAutoInboundFlowControl(); // called after start -> throws\n// after\npublic void beforeStart(ClientCallStreamObserver<Req> obs) { obs.disableAutoInboundFlowControl(); }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { observer.disableAutoInboundFlowControl(); } catch (IllegalStateException e) { throw new IllegalStateException(\"Disable auto flow control in ClientResponseObserver.beforeStart\", e); }","preventionTips":["Call disableAutoRequestWithInitial in beforeStart only","Design wrappers to configure before call start","Keep manual request() logic initialized from stream start"],"tags":["grpc","flow-control","illegal-state","observer"],"backgroundTag":"invalid-state-transition","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}