Skip to content

tzutil console window flash on every gh invocation (Windows) #13354

@adehad

Description

@adehad

Describe the bug

On Windows, every gh invocation that records telemetry produces a brief console window flash. The flash is tzutil.exe /g being spawned by the gh send-telemetry subprocess to resolve the local IANA timezone (via the transitive dependency github.com/thlib/go-timezone-local).

With Windows Terminal configured to keep windows open after exit (e.g. closeOnExit: never / graceful), these conhost instances accumulate over time as orphan terminals.

gh --version output: gh version 2.81.0 (from C:\Program Files\GitHub CLI\gh.exe).

OS: Windows 11 Enterprise 10.0.26200.

Steps to reproduce the behavior

  1. On Windows, configure Windows Terminal default profile with "closeOnExit": "never" (or use a setup that does not auto-close console windows on exit).
  2. Run any gh command — for example gh pr list.
  3. Observe a brief console window flash. With the non-auto-close terminal config, the orphan window remains.
  4. Repeat — each gh invocation produces another flash.

Tools that invoke gh periodically (e.g. VS Code GitHub extensions) cause this to occur every few minutes during normal use.

Expected vs actual behavior

Expected: gh send-telemetry runs as a fully headless background subprocess; no console window of any kind is visible to the user.

Actual: A tzutil.exe console window is briefly visible on every gh invocation that records telemetry.

Root cause

I traced the call chain by polling Win32_Process while running gh commands. Captured tree:

gh.exe (parent shell invocation)
└── gh.exe send-telemetry    (spawned with DETACHED_PROCESS — no console)
    └── tzutil.exe /g        (allocates fresh conhost — visible flash)

internal/telemetry/detach_windows.go spawns the telemetry subprocess with DETACHED_PROCESS. gh send-telemetry then transitively invokes tzlocal.localTZfromTzutil() from github.com/thlib/go-timezone-local, which performs exec.Command("tzutil", "/g") with no Windows-specific window-suppression flags.

Per the Win32 process creation flags reference:

  • DETACHED_PROCESS — child has no console at all. Any console-subsystem descendant (tzutil.exe) then allocates a fresh conhost on first stdio access, producing the visible flash.
  • CREATE_NO_WINDOW — child runs as a console application without a visible console window. Descendants inherit the non-visible console and don't allocate a new one.

The two flags are mutually exclusive; for this use case CREATE_NO_WINDOW is the correct choice.

Suggested fix

Replace DETACHED_PROCESS with CREATE_NO_WINDOW in internal/telemetry/detach_windows.go. CREATE_NEW_PROCESS_GROUP remains compatible with the new flag, so Ctrl+C semantics for the detached telemetry child are preserved.

PR open: #13353.

Workaround

Setting DO_NOT_TRACK=1 in the user environment disables telemetry entirely, suppressing the spawn (and the flash) — at the cost of disabling telemetry that maintainers may legitimately want.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority-3Affects a small number of users or is largely cosmetictelemetry

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions