{"record":{"id":"ca7a06d6d8682624","repo":"risingwavelabs/risingwave","slug":"unable-to-send-request","errorCode":null,"errorMessage":"unable to send request {}","messagePattern":"unable to send request (.+?)","errorType":"exception","errorClass":"RpcError","httpStatus":null,"severity":"error","filePath":"src/rpc_client/src/lib.rs","lineNumber":228,"sourceCode":"                    }\n                }\n            }\n        )*\n    }\n}\n\npub const DEFAULT_BUFFER_SIZE: usize = 16;\n\npub struct BidiStreamSender<REQ> {\n    tx: Sender<REQ>,\n}\n\nimpl<REQ> BidiStreamSender<REQ> {\n    pub async fn send_request<R: Into<REQ>>(&mut self, request: R) -> Result<()> {\n        self.tx\n            .send(request.into())\n            .await\n            .map_err(|_| anyhow!(\"unable to send request {}\", type_name::<REQ>()).into())\n    }\n}\n\npub struct BidiStreamReceiver<RSP> {\n    pub stream: Peekable<BoxStream<'static, Result<RSP>>>,\n}\n\nimpl<RSP> BidiStreamReceiver<RSP> {\n    pub async fn next_response(&mut self) -> Result<RSP> {\n        self.stream\n            .next()\n            .await\n            .ok_or_else(|| anyhow!(\"end of response stream\"))?\n    }\n}\n\npub struct BidiStreamHandle<REQ, RSP> {\n    pub request_sender: BidiStreamSender<REQ>,","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/rpc_client/src/lib.rs#L210-L246","documentation":"BidiStreamSender::send_request failed because the mpsc channel's receiver half was dropped, so the request (named by its Rust type via type_name) could not be delivered over the bidirectional stream. The send error is swallowed and replaced by this anyhow message wrapped in RpcError.","triggerScenarios":"Calling send_request after the server-side stream task holding the receiver has finished or errored; also when awaiting send on a closed bounded (DEFAULT_BUFFER_SIZE) channel whose consumer stopped.","commonSituations":"Remote meta/connector node restarted mid-session; long-lived BidiStreamHandle reused after an RPC error on the response side; client keeps sending after seeing a stream error elsewhere.","solutions":["Treat this as a broken stream: call BidiStreamHandle::initialize again to establish a fresh stream and resend the request.","Check server (meta/connector) logs for the task that terminated the stream.","Stop sending on the old handle once any response-stream error is observed; guard with a closed flag in callers that loop."],"exampleFix":"// before\nself.tx.send(request.into()).await.map_err(|_| anyhow!(\"unable to send request {}\", type_name::<REQ>()).into())\n// after: propagate cause and advise re-initialization\nself.tx.send(request.into()).await.map_err(|_| {\n    anyhow!(\"unable to send request {} (stream closed; re-initialize the stream)\", type_name::<REQ>())\n})?.into()","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match handle.request_sender.send_request(req).await {\n    Err(e) if e.to_string().contains(\"unable to send request\") => {\n        handle = BidiStreamHandle::initialize(&client, first_req).await?;\n        handle.request_sender.send_request(req).await?;\n    }\n    other => other?,\n}","preventionTips":["Re-initialize the stream whenever any RPC on it fails; do not keep sending.","Add liveness monitoring of the remote service so dead streams are detected early.","Bound outstanding requests so a dead stream surfaces quickly."],"tags":["rpc","streaming","channel-closed","grpc"],"backgroundTag":"broken-pipe","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}