FiloSottile/age · warning

SetConsoleMode failed

Error message

SetConsoleMode failed

What it means

On Windows, the terminal enablement code calls the Win32 SetConsoleMode API to turn on processed output and virtual terminal (ANSI) processing for stdout. If the syscall returns zero (failure), it reports "SetConsoleMode failed". This typically means the output handle is not a real console (redirected/piped) or the Windows version lacks VT support.

Source

Thrown at internal/term/term_windows.go:44

			ENABLE_PROCESSED_OUTPUT            uint32 = 0x1
			ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4
		)

		kernel32DLL := windows.NewLazySystemDLL("Kernel32.dll")
		setConsoleMode := kernel32DLL.NewProc("SetConsoleMode")

		var mode uint32
		if err := syscall.GetConsoleMode(syscall.Handle(out.Fd()), &mode); err != nil {
			return err
		}

		mode |= ENABLE_PROCESSED_OUTPUT
		mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING

		// If the SetConsoleMode function fails, the return value is zero.
		// See https://learn.microsoft.com/en-us/windows/console/setconsolemode#return-value.
		if ret, _, _ := setConsoleMode.Call(out.Fd(), uintptr(mode)); ret == 0 {
			return errors.New("SetConsoleMode failed")
		}
		return nil
	}
}

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Check Windows.GetConsoleMode first: only attempt SetConsoleMode when the handle is a real console; treat failure on non-console handles as non-fatal.
  2. Remove ENABLE_VIRTUAL_TERMINAL_PROCESSING and retry with just ENABLE_PROCESSED_OUTPUT on legacy consoles.
  3. Run in a Windows 10+ terminal (Windows Terminal) that supports VT processing.

Example fix

// before
if ret, _, _ := setConsoleMode.Call(out.Fd(), uintptr(mode)); ret == 0 {
    return errors.New("SetConsoleMode failed")
}
// after
if ret, _, _ := setConsoleMode.Call(out.Fd(), uintptr(mode)); ret == 0 {
    return nil // not a console or unsupported: degrade gracefully
}
Defensive patterns

Strategy: fallback

Validate before calling

var mode uint32
if err := windows.GetConsoleMode(windows.Handle(os.Stdout.Fd()), &mode); err != nil {
    // not a console: skip VT setup entirely
}

Type guard

func isConsole(f *os.File) bool {
    var mode uint32
    return windows.GetConsoleMode(windows.Handle(f.Fd()), &mode) == nil
}

Try / catch

if err := setupConsole(out); err != nil {
    // degrade gracefully: ANSI codes may not render, but proceed
    log.Printf("console VT setup unavailable: %v", err)
}

Prevention

When it happens

Trigger: Calling into internal/term's console setup on Windows when out.Fd() is a non-console handle (pipe, file) or the legacy console does not support ENABLE_VIRTUAL_TERMINAL_PROCESSING.

Common situations: Running an age plugin with stdout redirected to a file or pipe; very old Windows (pre-Windows 10) consoles; embedded terminal emulators that do not emulate console mode.

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/5cb0ac5ce1ceb6ef. Report an issue: GitHub.