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
- Run the editor from a real console session so a stdin handle exists
- If detaching is intentional, allocate a console first (AllocConsole) or open CONIN$ directly instead of relying on GetStdHandle
- Check how the process is spawned and stop passing STD_ERROR_HANDLE/closed handles as stdin
- 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
- Run GUI-launched processes with a console or allocate one with AllocConsole
- Check handle validity with GetConsoleWindow/GetStdHandle before console work
- Avoid spawning the editor with closed stdin handles
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
- Failed to set stdin to CONIN$
- Failed to duplicate stdin handle
- Failed to open CONIN$
- io::Error::last_os_error()
- No data piped to stdin
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)