git-ecosystem/git-credential-manager · error · IOException

Failed to open for reading.

Error message

Failed to open {TtyDeviceName} for reading.

What it means

PosixAnsiConsoleInput's constructor opens the terminal device (e.g. /dev/tty) with O_RDWR via PosixFileDescriptor to read keystrokes. If the open fails (invalid descriptor), it disposes and throws IOException so terminal interaction can fail fast rather than silently losing input.

Solutions

  1. Run the command in a terminal with an attached TTY (interactive SSH or local shell, `docker run -it`).
  2. Configure a non-interactive authentication mode (GCM_INTERACTIVE=never / credential.guiHelper or a different credential store) so no terminal input is needed.
  3. Check /dev/tty exists and is accessible: `ls -l /dev/tty`; fix device permissions or run within a proper session.
  4. Ensure stdin isn't redirected from /dev/null when the helper needs terminal prompts.

Example fix

// before (CI, no TTY)
$ git push   # GCM tries to prompt -> IOException
// after
$ export GCM_INTERACTIVE=never
$ git config credential.credentialStore cache  # or run in an interactive shell
Defensive patterns

Strategy: try-catch

Validate before calling

bool HasTty() => File.Exists("/dev/tty") &&
    SysCall.isatty(SysCall.open("/dev/tty", OpenFlags.O_RDWR));

Try / catch

try { using var input = new PosixAnsiConsoleInput(); ... }
catch (IOException ex) when (ex.Message.Contains("Failed to open"))
{ // no controlling terminal: use non-interactive flow }
return ExitNonInteractiveFallback();

Prevention

When it happens

Trigger: Constructing PosixAnsiConsoleInput when TtyDeviceName (typically /dev/tty) cannot be opened read-write: no controlling terminal, or permission denied on the device node.

Common situations: Running git/GCM under cron, systemd, CI, or an SSH command without a TTY; detached processes with stdin redirected away; containers without a tty allocation (`docker run` without -t); restricted /dev permissions.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11). Data as JSON: /api/errors/0f990f9fe11e3b17. Report an issue: GitHub.

Appendix: source

Thrown at src/Core/Interop/Posix/PosixAnsiConsoleInput.cs:94

    // Raw mode is reference-counted: every keystroke read and every interactive
    // session (see BeginRawModeSession) takes a hold, and the terminal is only
    // returned to cooked mode once the last hold is released. Holding raw mode
    // across a whole prompt — rather than re-entering it per keystroke — stops
    // the driver echoing fast-typed characters in the cooked window that would
    // otherwise open between reads. Guarded by _rawModeLock.
    private int _rawDepth;
    private IDisposable _rawMode;

    protected PosixAnsiConsoleInput()
    {
        PlatformUtils.EnsurePosix();

        _fd = new PosixFileDescriptor(TtyDeviceName, OpenFlags.O_RDWR);
        if (_fd.IsInvalid)
        {
            _fd.Dispose();
            throw new System.IO.IOException($"Failed to open {TtyDeviceName} for reading.");
        }

        // The terminal is left in its normal (cooked) mode except during an
        // active keystroke read (see ReadKey). That is what keeps Ctrl+C working
        // when GCM is doing other work — MSAL polling, a GUI, the network — and
        // not just while a Spectre prompt happens to be reading: in cooked mode
        // the terminal driver raises SIGINT and the runtime's default
        // disposition terminates the process.
        //
        // These handlers exist to restore the terminal if the process is killed
        // by a signal *while* a read is holding raw mode; the runtime does not
        // unwind our finally on a fatal signal, so without them the user's
        // terminal would be left in raw mode. SIGINT is included: although a
        // Ctrl+C keypress during a read cannot raise it (ISIG is off — Ctrl+C
        // arrives as a 0x03 byte we turn into an InterruptedException), an
        // external SIGINT (kill -INT, or one delivered to the foreground
        // process group) can still arrive mid-read. The handler restores only
        // when a read is active and otherwise no-ops, letting the runtime's

View on GitHub (pinned to e8ce762cd0)