{"record":{"id":"840989b3594bab93","repo":"tokio-rs/tokio","slug":"bytes-remaining-on-stream","errorCode":null,"errorMessage":"bytes remaining on stream","messagePattern":"bytes remaining on stream","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"tokio-util/src/codec/decoder.rs","lineNumber":151,"sourceCode":"    /// frames _across_ eof boundaries on sources that can be resumed.\n    ///\n    /// Note that the `buf` argument may be empty. If a previous call to\n    /// `decode_eof` consumed all the bytes in the buffer, `decode_eof` will be\n    /// called again until it returns `None`, indicating that there are no more\n    /// frames to yield. This behavior enables returning finalization frames\n    /// that may not be based on inbound data.\n    ///\n    /// Once `None` has been returned, `decode_eof` won't be called again until\n    /// an attempt to resume the stream has been made, where the underlying stream\n    /// actually returned more data.\n    fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        match self.decode(buf)? {\n            Some(frame) => Ok(Some(frame)),\n            None => {\n                if buf.is_empty() {\n                    Ok(None)\n                } else {\n                    Err(io::Error::new(io::ErrorKind::Other, \"bytes remaining on stream\").into())\n                }\n            }\n        }\n    }\n\n    /// Provides a [`Stream`] and [`Sink`] interface for reading and writing to this\n    /// `Io` object, using `Decode` and `Encode` to read and write the raw data.\n    ///\n    /// Raw I/O objects work with byte sequences, but higher-level code usually\n    /// wants to batch these into meaningful chunks, called \"frames\". This\n    /// method layers framing on top of an I/O object, by using the `Codec`\n    /// traits to handle encoding and decoding of messages frames. Note that\n    /// the incoming and outgoing frame types may be distinct.\n    ///\n    /// This function returns a *single* object that is both `Stream` and\n    /// `Sink`; grouping this into a single object is often useful for layering\n    /// things like gzip or TLS, which require both read and write access to the\n    /// underlying object.","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/tokio-rs/tokio/blob/625954f365727668cb02d04172b34f1149637728/tokio-util/src/codec/decoder.rs#L133-L169","documentation":"Runtime error from the default `Decoder::decode_eof` in tokio-util. When the stream reaches EOF and `decode` returns `Ok(None)` (no full frame) but the buffer still holds unconsumed bytes, this error is returned. It signals a truncated/partial frame at end of stream — a protocol framing mismatch or a peer that disconnected mid-message.","triggerScenarios":"A `FramedRead`/`Decoder` reaches EOF with leftover bytes that do not form a complete frame: e.g. a length-delimited stream whose last header promised more bytes than arrived, or a custom codec with a partial frame buffered at EOF.","commonSituations":"Peer closed the TCP connection mid-message; corrupt/truncated stream; codec framing (length-field size, endianness, delimiter) not matching the wire protocol; reading a file that was cut off; `LinesCodec` overrides `decode_eof` so this does not fire for plain lines.","solutions":["Verify the peer sends complete frames and closes cleanly.","If trailing partial bytes are acceptable, override `decode_eof` in your `Decoder` to return `Ok(None)` instead of erroring.","Confirm the codec framing matches the wire protocol (field length, endianness, offsets, delimiters).","Handle the `io::Error` (kind `Other`) on the stream and decide whether to log, discard, or reconnect."],"exampleFix":"// before: relying on default decode_eof, partial frame aborts the stream\nimpl Decoder for MyCodec {\n    type Item = Frame; type Error = io::Error;\n    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> { /* ... */ }\n}\n\n// after: tolerate trailing partial bytes at EOF\nimpl Decoder for MyCodec {\n    type Item = Frame; type Error = io::Error;\n    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> { /* ... */ }\n    fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> {\n        if buf.is_empty() { Ok(None) } else { /* drain/return last partial, or */ Ok(None) }\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// Narrow the partial-frame-at-EOF error\nfn is_bytes_remaining(e: &io::Error) -> bool {\n    e.kind() == io::ErrorKind::Other && e.to_string() == \"bytes remaining on stream\"\n}","tryCatchPattern":"while let Some(item) = framed.next().await {\n    match item {\n        Ok(frame) => handle(frame),\n        Err(e) if is_bytes_remaining(&e) => { /* partial frame at EOF: log and stop */ break; }\n        Err(e) => return Err(e.into()),\n    }\n}","preventionTips":["Confirm the codec framing exactly matches the wire protocol before relying on the default `decode_eof`.","Override `decode_eof` if trailing partial bytes are acceptable for your protocol.","Treat peer disconnects mid-frame as expected on untrusted networks."],"tags":["rust","tokio","tokio-util","codec","framing","network","runtime"],"backgroundTag":null,"analyzedSha":"625954f365727668cb02d04172b34f1149637728","analyzedAt":"2026-08-11T17:46:45.378Z","contentChangedAt":"2026-08-11T17:46:45.378Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}