{"record":{"id":"dcd6002146a4c2f9","repo":"rustfs/rustfs","slug":"get-object-streaming-body-stall-timeout","errorCode":null,"errorMessage":"get object streaming body stall timeout","messagePattern":"get object streaming body stall timeout","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"rustfs/src/app/object_usecase.rs","lineNumber":1782,"sourceCode":"            && std::future::Future::poll(timer.as_mut(), cx).is_ready()\n        {\n            self.timer = None;\n            warn!(\n                event = EVENT_GET_OBJECT_STREAM_BODY,\n                component = LOG_COMPONENT_APP,\n                subsystem = LOG_SUBSYSTEM_OBJECT,\n                request_id = %self.request_id,\n                range = %self.content_range.as_deref().unwrap_or(\"full\"),\n                size_bucket = get_object_stream_size_bucket(self.expected),\n                expected = self.expected,\n                emitted = self.emitted,\n                elapsed_ms = self.elapsed().as_millis(),\n                timeout_ms = self.timeout.as_millis(),\n                state = \"stall_timeout\",\n                \"GetObject streaming body stalled\"\n            );\n            self.finish_err();\n            return Poll::Ready(Err(std::io::Error::new(\n                std::io::ErrorKind::TimedOut,\n                \"get object streaming body stall timeout\",\n            )));\n        }\n\n        Poll::Pending\n    }\n}\n\nimpl<R: AsyncRead + Unpin> AsyncRead for GetObjectStreamingReader<R> {\n    fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<std::io::Result<()>> {\n        let filled_before = buf.filled().len();\n\n        loop {\n            // An armed resume owns the reader until it swaps in a reopened\n            // stream or exhausts its budget; the failed inner stream is never\n            // polled again.\n            if self.resume_in_flight() {","sourceCodeStart":1764,"sourceCodeEnd":1800,"githubUrl":"https://github.com/rustfs/rustfs/blob/9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de/rustfs/src/app/object_usecase.rs#L1764-L1800","documentation":"GetObjectStreamingReader wraps the object stream handed to the client with a stall watchdog: if no bytes are emitted for the configured timeout window, it fails the body with ErrorKind::TimedOut ('stall timeout') and logs expected/emitted/elapsed/timeout with state=stall_timeout. This protects the server from holding slots and buffers for clients (or intermediaries) that stop reading.","triggerScenarios":"A client that stops reading the response body (backpressure with no progress) for longer than the stall timeout; a proxy/CDN that buffers the entire object before forwarding; a network path that blackholes the connection without TCP failure.","commonSituations":"Download clients that process into a slow sink without reading the socket; nginx/traefik-style full-response buffering on large objects; mobile/flaky networks; aggressive firewall idle-timeouts between server and client.","solutions":["Make the client consume the body continuously (stream to disk, bounded buffers) instead of reading then pausing","Disable or tune whole-response buffering on any reverse proxy in front of the server","Raise the GET streaming stall timeout configuration if legitimately moving huge objects over slow links","Use ranged GETs / part-sized downloads so each request is short-lived and independently retryable"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match body.read(...).await {\n    Err(e) if e.kind() == std::io::ErrorKind::TimedOut\n        && e.to_string().contains(\"stall timeout\") => {\n        // resume from the last byte received using a ranged GET\n        resume_ranged_get(key, bytes_so_far).await\n    }\n    other => other,\n}","preventionTips":["Consume response bodies continuously; never park a GET stream while doing long local work","Disable full-response buffering on proxies between client and server","Size the stall timeout above your worst legitimate processing pause per chunk"],"tags":["rust","s3","get-object","timeout","backpressure","streaming"],"backgroundTag":"download-stall-timeout","analyzedSha":"9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de","analyzedAt":"2026-08-16T20:34:17.560Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}