{"record":{"id":"b39003b4dc1f8540","repo":"sigoden/aichat","slug":"failed-to-flush-osc52-sequence","errorCode":null,"errorMessage":"Failed to flush OSC52 sequence","messagePattern":"Failed to flush OSC52 sequence","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/utils/clipboard.rs","lineNumber":34,"sourceCode":"                clipboard.set_text(text)?;\n                #[cfg(target_os = \"linux\")]\n                std::thread::sleep(std::time::Duration::from_millis(50));\n                Ok(())\n            }\n            None => set_text_osc52(text),\n        }\n    }\n\n    /// Attempts to set text to clipboard with OSC52 escape sequence\n    /// Works in many modern terminals, including over SSH.\n    fn set_text_osc52(text: &str) -> anyhow::Result<()> {\n        let encoded = STANDARD.encode(text);\n        let seq = format!(\"\\x1b]52;c;{encoded}\\x07\");\n        if let Err(e) = std::io::Write::write_all(&mut std::io::stdout(), seq.as_bytes()) {\n            return Err(anyhow::anyhow!(\"Failed to send OSC52 sequence\").context(e));\n        }\n        if let Err(e) = std::io::Write::flush(&mut std::io::stdout()) {\n            return Err(anyhow::anyhow!(\"Failed to flush OSC52 sequence\").context(e));\n        }\n        Ok(())\n    }\n}\n\n#[cfg(any(target_os = \"android\", target_os = \"emscripten\"))]\nmod internal {\n    pub fn set_text(_text: &str) -> anyhow::Result<()> {\n        Err(anyhow::anyhow!(\"No clipboard available\"))\n    }\n}\n\npub fn set_text(text: &str) -> anyhow::Result<()> {\n    internal::set_text(text).context(\"Failed to copy\")\n}\n","sourceCodeStart":16,"sourceCodeEnd":50,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/utils/clipboard.rs#L16-L50","documentation":"This error is raised by `set_text_osc52` (via the platform `internal::set_text` behind the public `set_text`) after writing an OSC 52 escape sequence to stdout, when `std::io::stdout().flush()` fails. OSC 52 copy-to-clipboard works by emitting an escape sequence that the terminal itself must consume, so flushing stdout is what actually delivers the sequence. A flush failure means the process could not push the bytes out to the terminal (typically because stdout is closed, broken, or blocked).","triggerScenarios":"Calling `set_text` on a platform using the OSC 52 path when `stdout().flush()` returns an error: stdout is a closed pipe, the terminal was killed mid-write, or stdout is redirected to a file/socket that rejects the write.","commonSituations":"Running the CLI inside `head`/`grep` pipelines where stdout closes early; SSH sessions with a dying connection; output redirected to a file instead of a TTY so no terminal is there to consume OSC 52.","solutions":["Run the program with stdout attached to a live interactive terminal instead of a closed pipe or redirect.","Check that the parent shell/SSH session is still alive and that stdout is not broken.","Use an alternative clipboard backend (e.g. a native clipboard crate) when no TTY is available.","Catch the error and warn instead of failing the whole command, since clipboard copy is usually non-essential."],"exampleFix":"// before\nlet seq = format!(\"\\x1b]52;c;{encoded}\\x07\");\nstd::io::Write::write_all(&mut std::io::stdout(), seq.as_bytes())?;\nstd::io::Write::flush(&mut std::io::stdout())?;\n// after\nif let Err(e) = std::io::Write::write_all(&mut std::io::stdout(), seq.as_bytes())\n    .and_then(|_| std::io::Write::flush(&mut std::io::stdout()))\n{\n    eprintln!(\"warning: could not copy to clipboard via OSC52: {e}\");\n}","handlingStrategy":"try-catch","validationCode":"use std::io::IsTerminal;\nfn osc52_viable() -> bool {\n    std::io::stdout().is_terminal()\n}","typeGuard":"fn stdout_is_usable() -> bool {\n    std::io::stdout().is_terminal()\n}","tryCatchPattern":"match set_text(text) {\n    Ok(()) => println!(\"Copied to clipboard\"),\n    Err(e) if e.to_string().contains(\"flush OSC52\") =>\n        eprintln!(\"warning: terminal did not accept clipboard data: {e}\"),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Check stdout is a TTY (io::IsTerminal) before using the OSC 52 backend.","Fall back to a native clipboard backend when stdout is redirected.","Treat clipboard copy as best-effort: warn, don't abort the command.","Avoid piping the CLI into commands that close stdout early when copying."],"tags":["clipboard","stdout","osc52","terminal"],"backgroundTag":"broken-pipe","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}