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
- Run the command in a terminal with an attached TTY (interactive SSH or local shell, `docker run -it`).
- Configure a non-interactive authentication mode (GCM_INTERACTIVE=never / credential.guiHelper or a different credential store) so no terminal input is needed.
- Check /dev/tty exists and is accessible: `ls -l /dev/tty`; fix device permissions or run within a proper session.
- 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
- Check for an interactive terminal (isatty) before invoking interactive credential helpers.
- In CI/cron/docker, set GCM_INTERACTIVE=never or pre-provision credentials.
- Allocate a TTY (ssh -t, docker run -it, script -c) when interaction is required.
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
- Failed to open for writing.
- Failed to read initial terminal settings.
- Failed to enter raw terminal mode.
- Failed to read initial terminal settings.
- Failed to enter raw terminal mode.
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'sView on GitHub (pinned to e8ce762cd0)