Skip to content

Windows: copilot.exe exits 1 with empty streams when stdout is a pipe / regular file (FlushFileBuffers ERROR_INVALID_FUNCTION) — breaks all non-PowerShell automation on v1.0.44-0 #3188

@dipakboyed

Description

@dipakboyed

Summary

GitHub Copilot CLI (copilot.exe, v1.0.44-0 on Windows) exits with code 1 and produces empty stdout / empty stderr when invoked from a child process whose stdout handle is anything other than a PowerShell native > redirect (or an interactive console).

When stdout is set to DEVNULL and stderr is captured separately, the underlying error becomes visible:

Error: Failed to sync '<stdout>': Incorrect function.

This is ERROR_INVALID_FUNCTION (Win32 error 1) returned by FlushFileBuffers on a handle that does not support buffer flushing — typically anonymous pipes, NUL device, or files opened without the right flags. The CLI appears to call a sync/flush operation on stdout at exit and treats its failure as a fatal error, dropping the actual response from stdout and never writing the error to stderr unless stderr is captured independently.

This breaks every CI/automation/daemon scenario on Windows that uses subprocess.Popen (Python), Process.Start (.NET), child_process.spawn (Node), or any other launcher that creates pipe / regular-file stdout handles.

Environment

  • Copilot CLI: 1.0.44-0 (copilot --version)
  • OS: Windows 11
  • Install: WinGet (copilot.exe is a native binary, not an npm shim)
  • Auth: copilot auth shows a valid GitHub login

Reproduction

Working (PowerShell direct, file redirect)

copilot --prompt "respond with one word ok" --model claude-sonnet-4.5 --allow-all --no-ask-user > out.txt 2> err.txt
$LASTEXITCODE   # 0 (success)

Failing (Python subprocess, PIPE)

import subprocess
proc = subprocess.Popen(
    ["copilot", "--prompt", "respond with one word ok",
     "--model", "claude-sonnet-4.5", "--allow-all", "--no-ask-user"],
    stdin=subprocess.DEVNULL,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True,
)
out, err = proc.communicate(timeout=120)
print(proc.returncode)   # 1
print(repr(out))         # ''
print(repr(err))         # ''

Failing (Python subprocess, regular file)

import subprocess
out_f = open("out.txt", "wb")
err_f = open("err.txt", "wb")
proc = subprocess.Popen(
    ["copilot", "--prompt", "respond with one word ok",
     "--model", "claude-sonnet-4.5", "--allow-all", "--no-ask-user"],
    stdin=subprocess.DEVNULL, stdout=out_f, stderr=err_f,
)
proc.wait(timeout=120)
print(proc.returncode)         # 1
print(open("out.txt","rb").read())   # b''
print(open("err.txt","rb").read())   # b''  (no message at all)

Failing (cmd.exe with redirect)

proc = subprocess.Popen(
    ["cmd.exe", "/c",
     'copilot --prompt "respond with one word ok" --model claude-sonnet-4.5 --allow-all --no-ask-user > out.txt 2> err.txt'],
    stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
)
proc.communicate(timeout=120)
# Exit 1, no files created.

Surfaces the actual error (Python subprocess, DEVNULL stdout + PIPE stderr)

proc = subprocess.Popen(
    ["copilot", "--prompt", "respond with one word ok",
     "--model", "claude-sonnet-4.5", "--allow-all", "--no-ask-user"],
    stdin=subprocess.DEVNULL,
    stdout=subprocess.DEVNULL,
    stderr=subprocess.PIPE,
    text=True,
)
out, err = proc.communicate(timeout=120)
print(proc.returncode)   # 1
print(err)
# Error: Failed to sync '<stdout>': Incorrect function.

Working (Python subprocess, wrapped in pwsh.exe -c "... > out 2> err")

proc = subprocess.Popen(
    ["pwsh.exe", "-NoProfile", "-NonInteractive", "-Command",
     'copilot --prompt "respond with one word ok" --model "claude-sonnet-4.5" --allow-all --no-ask-user > out.txt 2> err.txt ; exit $LASTEXITCODE'],
    stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
)
proc.communicate(timeout=180)
# Exit 0, out.txt has response, err.txt has the normal token-summary footer.

This confirms the workaround and isolates the bug to how the CLI's runtime treats the stdout handle when it isn't the kind PowerShell's native > operator creates.

Expected behavior

  • The CLI should not call FlushFileBuffers (or equivalent) on stdout when the handle does not support it. GetFileType() on the handle would let it skip the flush for FILE_TYPE_PIPE, FILE_TYPE_CHAR, etc.
  • Failing that, a flush failure at exit should not be promoted to a fatal exit-1 — the actual response data has already been written.
  • At minimum, the error message should be flushed to stderr (with a flush() before exit()) so consumers can diagnose the failure. Today the message is lost in PIPE/PIPE mode because the panic path apparently aborts before stderr is flushed.

Actual behavior

  • Exit code 1 with no output on either stream
  • Buffered token-summary footer that normally goes to stderr is also lost
  • Every CI/automation tool on Windows fails silently

Impact

This blocks all programmatic use of Copilot CLI from Windows automation:

  • Daemons / background workers (e.g., PR-review bots that invoke copilot)
  • CI pipelines that aren't wrapped in PowerShell
  • IDE integrations that use child_process.spawn / equivalent
  • Any non-interactive scenario using Python, Node, .NET, Go, Rust, etc.

The current workaround (wrap every invocation in pwsh.exe -NoProfile -NonInteractive -Command "... > out 2> err") adds an extra process per call and is fragile (string-encoded args, encoding issues with the temp files, etc.).

Suggested fix

In the CLI's process-exit / stream-flush path, gate the explicit stdout sync on GetFileType(stdout) == FILE_TYPE_DISK, or wrap it in a try/catch that does not promote the failure to exit code 1 (since the data has already been written).

Independently, make sure stderr is flushed before exit so that any startup / runtime error is observable from automation.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:non-interactiveNon-interactive mode (-p), CI/CD, ACP protocol, and headless automationarea:platform-windowsWindows-specific: PowerShell, cmd, Git Bash, WSL, Windows Terminal

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions