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

  1. Verify the stdin handle is still valid right before duplicating; re-fetch it if necessary
  2. Ensure DUPLICATE_SAME_ACCESS and non-inheritable flags are correct and buffers/pointers are valid
  3. Check whether an antivirus/sandbox is interfering with handle duplication
  4. 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

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


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 input

View on GitHub (pinned to 67894ca546)