{"record":{"id":"5c675cafef8a580c","repo":"bensadeh/tailspin","slug":"stream-thread-panicked","errorCode":null,"errorMessage":"stream thread panicked","messagePattern":"stream thread panicked","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"src/main.rs","lineNumber":65,"sourceCode":"    let (initial_read_tx, _) = mpsc::channel();\n\n    BrokenPipe::suppress(process_stream(reader, writer, highlighter, initial_read_tx))\n}\n\n/// Runs the stream on its own thread while the pager runs as a child process;\n/// whichever finishes first decides what happens to the other.\nfn run_with_pager(reader: Reader, writer: Writer, highlighter: Highlighter, pager: Pager) -> anyhow::Result<()> {\n    let exec_child = reader.exec_child();\n    let (initial_read_tx, initial_read_rx) = mpsc::channel();\n    let (events_tx, events) = mpsc::channel();\n\n    let stream_tx = events_tx.clone();\n    thread::spawn(move || {\n        // A panic must still produce an event, or the recv loop blocks forever\n        let result = catch_unwind(AssertUnwindSafe(|| {\n            process_stream(reader, writer, &highlighter, initial_read_tx)\n        }))\n        .unwrap_or_else(|_| Err(anyhow::anyhow!(\"stream thread panicked\")));\n        let _ = stream_tx.send(Event::Stream(result));\n    });\n\n    if initial_read_rx.recv().is_err() {\n        let Event::Stream(result) = events.recv()? else {\n            unreachable!(\"the pager is not spawned yet\")\n        };\n        return result;\n    }\n\n    let pager_child = match pager.spawn() {\n        Ok(pager_child) => pager_child,\n        Err(e) => {\n            kill_exec_child(exec_child.as_deref());\n            return Err(e);\n        }\n    };\n    let waiter = pager_child.waiter();","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/bensadeh/tailspin/blob/8ecaa9a8a1bf3ba1e7457a20de11d2df75327d81/src/main.rs#L47-L83","documentation":"This error is a synthesized fallback: when the streaming worker thread panics while processing the reader output, catch_unwind captures the panic and the code substitutes this anyhow error instead of letting the recv loop block forever. It means the stream-processing logic crashed unexpectedly, not that the underlying data was bad.","triggerScenarios":"A panic (unwrap on None, index out of bounds, assertion, explicit panic) inside process_stream executed on the spawned thread; catch_unwind converts the unwind into Err(anyhow!(\"stream thread panicked\")).","commonSituations":"Highlighter bugs on unusual input (e.g. malformed ANSI or very long lines); a regression in process_stream that unwraps on an empty batch; memory-pressure aborts surfacing as panics in dependent code paths.","solutions":["Reproduce with the same input and get the panic message backtrace (set RUST_BACKTRACE=1) to find the real crash site","Fix the panicking code path in process_stream/highlighter to return Result instead of unwrapping","Check the input data for edge cases (empty input, invalid UTF-8, extremely large lines) that trigger the panic","File a bug with the input and backtrace if the panic is inside the library code"],"exampleFix":"// before\nlet first = batch.lines.first().unwrap();\n// after\nlet Some(first) = batch.lines.first() else { return Ok(()); };","handlingStrategy":"try-catch","validationCode":"RUST_BACKTRACE=1 myapp --exec \"...\" 2>panic.log  # capture the real panic location first","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string() == \"stream thread panicked\" => {\n        eprintln!(\"stream crashed; see backtrace with RUST_BACKTRACE=1\");\n        // fall back to rendering without the streaming path\n    }\n    Err(e) => return Err(e),\n    Ok(_) => {}\n}","preventionTips":["Avoid unwrap/expect/indexing in stream-processing code; return Result instead","Fuzz or test process_stream with edge-case inputs (empty, invalid UTF-8, huge lines)","Keep the panic-to-event catch_unwind safety net but also log the original panic payload","Set panic = \"abort\" only consciously; for this design keep unwind enabled so the fallback error can be produced"],"tags":["panic","threading","internal-error"],"backgroundTag":"thread-panicked","analyzedSha":"8ecaa9a8a1bf3ba1e7457a20de11d2df75327d81","analyzedAt":"2026-09-13T19:04:00.202Z","contentChangedAt":"2026-09-13T19:04:00.202Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}