{"record":{"id":"93f774a673074e6c","repo":"GitoxideLabs/gitoxide","slug":"at-least-in-theory","errorCode":null,"errorMessage":"at least in theory","messagePattern":"at least in theory","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-packetline/src/async_io/sidebands.rs","lineNumber":329,"sourceCode":"                                        None => {\n                                            return Poll::Ready(Err(io::Error::other(\n                                                \"encountered non-data line in a data-line only context\",\n                                            )));\n                                        }\n                                    };\n                                }\n                            }\n                        }\n                    }\n                };\n                this.cap = cap + ofs;\n                this.pos = ofs;\n            }\n        }\n        let range = self.pos..self.cap;\n        match &self.get_mut().state {\n            State::Idle { parent } => Poll::Ready(Ok(&parent.as_ref().expect(\"parent always available\").buf[range])),\n            State::ReadLine { .. } => unreachable!(\"at least in theory\"),\n        }\n    }\n\n    fn consume(self: Pin<&mut Self>, amt: usize) {\n        let this = self.get_mut();\n        this.pos = std::cmp::min(this.pos + amt, this.cap);\n    }\n}\n\nimpl<T, F> AsyncRead for WithSidebands<'_, T, F>\nwhere\n    T: AsyncRead + Unpin,\n    F: FnMut(bool, &[u8]) -> ProgressAction + Unpin,\n{\n    fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {\n        use std::io::Read;\n        let mut rem = ready!(self.as_mut().poll_fill_buf(cx))?;\n        let nread = rem.read(buf)?;","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-packetline/src/async_io/sidebands.rs#L311-L347","documentation":"`poll_fill_buf` (the async BufRead implementation for sideband-multiplexed packetline streams) asserts that the reader is never asked to fill its buffer while the internal state is `ReadLine` — i.e. while a line-based read is in progress. Hitting the panic means the stream's state machine was interleaved incorrectly, typically by mixing line reads and buffered reads on the same connection.","triggerScenarios":"Calling `fill_buf`/BufRead APIs while a `read_line`-style operation is mid-flight on the same pinned stream — e.g. polling fill_buf inside a ReadLine state without completing the line read, or concurrent/mixed use of line and buffered read modes on one packetline sideband.","commonSituations":"Hand-rolled protocol code mixing `read_line` and `fill_buf`/`read_until` on the same sideband stream; polling a future that owns the reader while another read is pending; spawning tasks sharing one connection without synchronization.","solutions":["Complete each line read before issuing buffered reads (or vice versa); don't interleave read_line with fill_buf on the same stream","Use the blocking/std implementation or the appropriate mode consistently for the protocol phase (line-based handshake vs binary sideband data)","Serialize access to the stream (own it in one task, or use a mutex) and report the panic to gix-packetline if it occurs with sequential reads"],"exampleFix":"// before\nlet line = reader.read_line(&mut buf).await?; // leaves state ReadLine\nlet chunk = reader.fill_buf().await?;          // panics\n// after\nlet line = reader.read_line(&mut buf).await?;\nreader.consume(line);\nlet chunk = reader.fill_buf().await?;","handlingStrategy":"type-guard","validationCode":"// only call fill_buf when no line read is in progress\ndebug_assert!(!matches!(stream.state(), ReadLine), \"cannot fill_buf during ReadLine state\");","typeGuard":"fn can_fill_buf<S: ReadMode>(s: &S) -> bool { matches!(s.state(), State::Idle { .. }) }","tryCatchPattern":"// panic occurs in poll; guard the call site instead\nif stream_is_idle(&stream) {\n    let buf = Pin::new(&mut stream).fill_buf().await?;\n} else {\n    return Err(io::Error::new(io::ErrorKind::InvalidInput, \"read in progress\"));\n}","preventionTips":["Never interleave read_line with fill_buf/read_until on the same packetline stream","Own the stream in a single task; avoid sharing it across concurrent futures","Use line mode for the handshake phase and buffered mode only for sideband data","If the panic occurs with strictly sequential reads, file a bug against gix-packetline"],"tags":["rust","async","packetline","sideband","panic"],"backgroundTag":"invalid-state-transition","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}