sinelaw/fresh · error

Failed to get stdin handle

Error message

Failed to get stdin handle

What it means

The editor failed to obtain the standard input handle on Windows via GetStdHandle(STD_INPUT_HANDLE). It bails out when the returned handle is INVALID_HANDLE_VALUE or null, since stdin cannot be used or redirected without it. This is a Windows-specific console setup step, typically performed when attaching stdin to a TTY.

Solutions

  1. Run the editor from a real console session so a stdin handle exists
  2. If detaching is intentional, allocate a console first (AllocConsole) or open CONIN$ directly instead of relying on GetStdHandle
  3. Check how the process is spawned and stop passing STD_ERROR_HANDLE/closed handles as stdin
  4. On non-Windows this code path does not run; verify you are on the expected platform build

Example fix

// before
let stdin_handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
// after
let stdin_handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
if stdin_handle == INVALID_HANDLE_VALUE || stdin_handle.is_null() {
    unsafe { AllocConsole(); }
    let stdin_handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
    if stdin_handle == INVALID_HANDLE_VALUE || stdin_handle.is_null() {
        anyhow::bail!("Failed to get stdin handle");
    }
}
Defensive patterns

Strategy: validation

Validate before calling

fn has_stdin_handle() -> bool {
    let h = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
    h != INVALID_HANDLE_VALUE && !h.is_null()
}

Type guard

fn is_valid_handle(h: HANDLE) -> bool {
    h != INVALID_HANDLE_VALUE && !h.is_null()
}

Try / catch

match try_setup_stdin() {
    Ok(()) => {},
    Err(e) => eprintln!("stdin unavailable: {e}"), // degrade gracefully
}

Prevention

When it happens

Trigger: Calling GetStdHandle(STD_INPUT_HANDLE) on Windows returns INVALID_HANDLE_VALUE or a null handle, e.g. when the process was started detached from any console (no stdin allocated) or stdin was already closed.

Common situations: Launching fresh-editor from a GUI without a console, from a service or scheduled task, or with stdin redirected to a closed handle; running under a runner that detaches the console.

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/d219f6b49870423f. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/src/main.rs:750

    // SAFETY: `pipe_fd` is a valid descriptor this function just created and
    // hands sole ownership of to the returned `File`.
    Ok(unsafe { std::fs::File::from_raw_fd(pipe_fd) })
}

/// Windows counterpart of [`take_stdin_pipe`].
#[cfg(windows)]
fn take_stdin_pipe() -> AnyhowResult<std::fs::File> {
    use std::os::windows::io::FromRawHandle;
    use windows_sys::Win32::Foundation::{
        DuplicateHandle, DUPLICATE_SAME_ACCESS, HANDLE, INVALID_HANDLE_VALUE,
    };
    use windows_sys::Win32::System::Console::{GetStdHandle, STD_INPUT_HANDLE};
    use windows_sys::Win32::System::Threading::GetCurrentProcess;

    // SAFETY: plain console/handle queries; every failure is checked below.
    let stdin_handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
    if stdin_handle == INVALID_HANDLE_VALUE || stdin_handle.is_null() {
        anyhow::bail!("Failed to get stdin handle");
    }

    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 {

View on GitHub (pinned to 67894ca546)