denoland/deno · error

GetHandleInformation failed (error {})

Error message

GetHandleInformation failed (error {})

What it means

During early startup on Windows, Deno probes stdin/stdout/stderr with GetStdHandle + GetHandleInformation to detect closed handles and substitute the NUL device. If GetHandleInformation fails with any error other than ERROR_INVALID_HANDLE, the handle state is too broken to recover from and Deno panics with the raw Win32 error code. It means a handle exists but its flags cannot be queried (access denied, bogus inherited handle).

Source

Thrown at cli/util/windows.rs:223

    use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING;
    use windows_sys::Win32::System::Console::GetStdHandle;
    use windows_sys::Win32::System::Console::STD_ERROR_HANDLE;
    use windows_sys::Win32::System::Console::STD_INPUT_HANDLE;
    use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE;
    use windows_sys::Win32::System::Console::SetStdHandle;

    for std_handle in [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE] {
      // Check whether stdio handle is open.
      let handle = GetStdHandle(std_handle);
      let is_valid = if handle.is_null() || handle == INVALID_HANDLE_VALUE {
        false
      } else {
        // The stdio handle is open; check whether its handle is valid.
        let mut flags: u32 = 0;
        match GetHandleInformation(handle, &mut flags) {
          FALSE if GetLastError() == ERROR_INVALID_HANDLE => false,
          FALSE => {
            panic!("GetHandleInformation failed (error {})", GetLastError());
          }
          _ => true,
        }
      };

      if !is_valid {
        // Open NUL device.
        let desired_access = match std_handle {
          STD_INPUT_HANDLE => FILE_GENERIC_READ,
          _ => FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
        };
        let security_attributes = SECURITY_ATTRIBUTES {
          nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
          lpSecurityDescriptor: std::ptr::null_mut(),
          bInheritHandle: TRUE,
        };
        let file_handle = CreateFileA(
          c"\\\\?\\NUL".as_ptr() as *const u8,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Launch Deno from a normal console to verify it works there
  2. When spawning Deno programmatically, always pass explicit stdin/stdout/stderr (pipe, file, or NUL) instead of inheriting broken handles
  3. Use explicit redirection in service contexts: `deno run app.ts < NUL > deno.log 2>&1`
  4. Translate the reported code with `net helpmsg <code>` to identify which handle/query failed; report to denoland/deno if it persists on the latest version

Example fix

:: before — scheduler/service launches deno with broken inherited stdio
deno run app.ts

:: after — every stream gets a valid target
deno run app.ts < NUL > deno.log 2>&1
Defensive patterns

Strategy: validation

Validate before calling

// Rust parent: never leave deno with broken inherited handles
use std::process::{Command, Stdio};
Command::new("deno")
  .args(["run", "app.ts"])
  .stdin(Stdio::null())
  .stdout(Stdio::piped())
  .stderr(Stdio::piped())
  .spawn()?;

Prevention

When it happens

Trigger: Launching `deno` from a parent that passes broken or pseudo stdio handles: Windows services, Task Scheduler, git hooks, CI agents, or hand-rolled CreateProcess callers that close or redirect stdio incorrectly; also security software intercepting handle queries.

Common situations: Running Deno as a scheduled task or service without explicit stdio redirection; spawning Deno from another program with closed handle slots; antivirus/EDR interference on Windows.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/b1d8afd2ebd1f5b7. Report an issue: GitHub.