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

Open

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

Activity

  1. added
    area:platform-windowsWindows-specific: PowerShell, cmd, Git Bash, WSL, Windows Terminal
    area:non-interactiveNon-interactive mode (-p), CI/CD, ACP protocol, and headless automation
    on May 7, 2026
  2. added theissue type on May 7, 2026
  3. polajenko commented on May 27, 2026

    @polajenko

    Ran into the same issue on v1.0.54 (Windows PowerShell). The response is completely absent from stdout in -p mode — only the stats footer is written. Confirmed by capturing raw bytes.

    Additional findings:

    • -s / --silent flag is also broken — produces empty output entirely
    • --screen-reader, --no-color, --no-custom-instructions all have the same result
    • The issue affects PowerShell piping (2>&1, Out-File, Out-String) — not just subprocess launchers

    Workaround that works:

    copilot -p "what is 1+1?" --yolo --output-format json |
      ForEach-Object { try { $_ | ConvertFrom-Json } catch {} } |
      Where-Object { $_.type -eq "assistant.message" } |
      ForEach-Object { $_.data.content }

    --output-format json bypasses the TUI renderer entirely and writes JSONL directly to stdout.

    Closing #3544 as a duplicate of this issue.

  4. patniko commented on Oct 26, 2026

    @patniko
    Collaborator

    Thanks for the report. We've shipped fixes in this area since this was filed, and we believe this may now be resolved. Could you retest on the latest release (Copilot CLI v1.0.103) and let us know whether you still see it? If we don't hear back in a couple of weeks, we may close this issue — you can always reopen it.

    Triaged as part of a Copilot SDK backlog review, with help from GitHub Copilot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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 Terminalmore-info-needed

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions