sinelaw/fresh · error
Failed to duplicate stdin handle
Error message
Failed to duplicate stdin handle: {} What it means
DuplicateHandle failed while duplicating the process's stdin handle (needed so a File can safely own it). The error message carries io::Error::last_os_error() describing the underlying Win32 failure. Without a duplicated handle the editor cannot reassign stdin.
Solutions
- Verify the stdin handle is still valid right before duplicating; re-fetch it if necessary
- Ensure DUPLICATE_SAME_ACCESS and non-inheritable flags are correct and buffers/pointers are valid
- Check whether an antivirus/sandbox is interfering with handle duplication
- Fall back to opening CONIN$ directly instead of duplicating the existing stdin
Example fix
// before
let ok = unsafe { DuplicateHandle(GetCurrentProcess(), stdin_handle, GetCurrentProcess(), &mut duplicated, 0, 0, DUPLICATE_SAME_ACCESS) };
if ok == 0 { anyhow::bail!("Failed to duplicate stdin handle: {}", io::Error::last_os_error()); }
// after
let stdin_handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
if stdin_handle == INVALID_HANDLE_VALUE || stdin_handle.is_null() {
anyhow::bail!("stdin handle unavailable; cannot duplicate");
}
let ok = unsafe { DuplicateHandle(GetCurrentProcess(), stdin_handle, GetCurrentProcess(), &mut duplicated, 0, 0, DUPLICATE_SAME_ACCESS) };
if ok == 0 { anyhow::bail!("Failed to duplicate stdin handle: {}", io::Error::last_os_error()); } Defensive patterns
Strategy: try-catch
Validate before calling
let h = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
if h == INVALID_HANDLE_VALUE || h.is_null() { eprintln!("stdin invalid; skip duplicate"); } Type guard
fn duplicatable(h: HANDLE) -> bool { !h.is_null() && h != INVALID_HANDLE_VALUE } Try / catch
if let Err(e) = duplicate_stdin() {
eprintln!("handle duplication failed: {e}; falling back to CONIN$");
} Prevention
- Re-fetch the stdin handle immediately before duplication
- Do not close stdin in parent/spawner code before the child initializes
- Test under sandbox/AV configurations that may block DuplicateHandle
When it happens
Trigger: Calling DuplicateHandle with the current process pseudo-handle on the stdin handle returns 0 — e.g. the source handle was already closed/invalid, or a privilege/handle-type issue prevents duplication.
Common situations: stdin handle closed by a parent process or wrapper script; racing close between GetStdHandle and DuplicateHandle; running inside a sandbox that restricts handle duplication.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- Failed to get stdin handle
- Failed to set stdin to CONIN$
- io::Error::last_os_error()
- Failed to open CONIN$
- No data piped to stdin
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/07e2daeecdc51a00.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/main.rs:769
}
let mut duplicated: HANDLE = std::ptr::null_mut();
// SAFETY: duplicating a handle this process owns into this process; the
// result is checked and then owned by the `File` below.
let ok = unsafe {
let me = GetCurrentProcess();
DuplicateHandle(
me,
stdin_handle,
me,
&mut duplicated,
0,
0, // not inheritable
DUPLICATE_SAME_ACCESS,
)
};
if ok == 0 {
anyhow::bail!(
"Failed to duplicate stdin handle: {}",
io::Error::last_os_error()
);
}
// SAFETY: `duplicated` is a valid handle this function just created and
// hands sole ownership of to the returned `File`.
Ok(unsafe { std::fs::File::from_raw_handle(duplicated.cast()) })
}
/// Check if stdin has data available (is a pipe or redirect, not a TTY)
fn stdin_has_data() -> bool {
use std::io::IsTerminal;
!io::stdin().is_terminal()
}
/// Reopen stdin from /dev/tty after reading piped content.
/// This allows crossterm to use the terminal for keyboard inputView on GitHub (pinned to 67894ca546)