{"record":{"id":"5e43423ecc5e556f","repo":"RightNow-AI/openfang","slug":"failed-to-draw","errorCode":null,"errorMessage":"Failed to draw","messagePattern":"Failed to draw","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/openfang-cli/src/tui/chat_runner.rs","lineNumber":792,"sourceCode":"\n    // Store the requested agent name for later resolution\n    if let Some(ref name) = agent_name {\n        state.agent_name = name.clone();\n    }\n\n    // Boot sequence: check for daemon, or boot kernel in-process\n    if let Some(base_url) = crate::find_daemon() {\n        state.resolve_daemon_agent(&base_url, agent_name.as_deref());\n    } else {\n        state.booting = true;\n        event::spawn_kernel_boot(config, tx);\n    }\n\n    // ── Main loop ────────────────────────────────────────────────────────────\n    while !state.should_quit {\n        terminal\n            .draw(|frame| state.draw(frame))\n            .expect(\"Failed to draw\");\n\n        match rx.recv_timeout(Duration::from_millis(33)) {\n            Ok(ev) => state.handle_event(ev),\n            Err(mpsc::RecvTimeoutError::Timeout) => {}\n            Err(mpsc::RecvTimeoutError::Disconnected) => break,\n        }\n        // Drain queued events\n        while let Ok(ev) = rx.try_recv() {\n            state.handle_event(ev);\n        }\n    }\n\n    ratatui::restore();\n}\n","sourceCodeStart":774,"sourceCodeEnd":807,"githubUrl":"https://github.com/RightNow-AI/openfang/blob/acf2587e46be174c10200489c9a2d23a39a98aeb/crates/openfang-cli/src/tui/chat_runner.rs#L774-L807","documentation":"ratatui's Terminal::draw() returns io::Result<CompletedFrame> and errors when writing to the terminal backend fails — e.g. stdout closed, not a TTY, resize/IO errors, or the terminal was disconnected. run_chat_tui panics via expect, aborting the TUI mid-loop.","triggerScenarios":"terminal.draw(|frame| state.draw(frame)) fails because stdout was closed/piped away after startup, the terminal backend write errored (EIO), or the process lost its controlling TTY while the chat TUI main loop is running.","commonSituations":"User closed the terminal or disconnected an SSH session while the TUI ran; running under a wrapper that kills the pty; stdout redirected to a file mid-session; broken pipe from a parent process.","solutions":["Detect broken-pipe/EIO and exit the TUI loop gracefully (set should_quit) instead of panicking.","Verify a TTY exists before entering run_chat_tui (atty/IsTerminal check) and fall back to non-TUI mode.","Handle SIGHUP/SIGTERM and terminal close events to quit the loop proactively.","Propagate the io::Error from run_chat_tui and print a user-friendly message."],"exampleFix":"// before\nterminal\n    .draw(|frame| state.draw(frame))\n    .expect(\"Failed to draw\");\n// after\nif terminal.draw(|frame| state.draw(frame)).is_err() {\n    break; // terminal gone; exit loop gracefully\n}","handlingStrategy":"try-catch","validationCode":"// Ensure stdout is a TTY before entering the TUI\nuse std::io::IsTerminal;\nfn tui_supported() -> bool {\n    std::io::stdout().is_terminal()\n}","typeGuard":null,"tryCatchPattern":"// break the loop on draw failure instead of panicking\nmatch terminal.draw(|frame| state.draw(frame)) {\n    Ok(_) => {}\n    Err(e) => {\n        eprintln!(\"terminal unavailable: {e}; exiting TUI\");\n        break;\n    }\n}","preventionTips":["Check std::io::stdout().is_terminal() before starting TUI mode; offer a non-TUI fallback.","Handle SIGINT/SIGHUP so the loop exits cleanly on session loss.","Write draw failures to a log file (stderr may be gone too).","Test TUI under SSH disconnect and piped-stdout scenarios."],"tags":["ratatui","tui","terminal","panic","io"],"backgroundTag":"terminal-draw-failed","analyzedSha":"acf2587e46be174c10200489c9a2d23a39a98aeb","analyzedAt":"2026-09-02T22:42:28.464Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}