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

Failed to read initial terminal settings.

Error message

Failed to read initial terminal settings.

What it means

LinuxAnsiConsoleInput.RawModeContext captures the terminal's current termios settings via tcgetattr so they can be restored after raw mode; if the tcgetattr syscall fails (non-zero return) the constructor throws IOException. Without the original settings the code cannot safely enter or later restore raw mode.

Solutions

  1. Run the tool in a real terminal (allocate a TTY: docker run -t, script/expect in CI)
  2. Check Isatty/Console.IsInputRedirected before requesting raw mode and fall back to non-interactive input
  3. Verify the fd passed to RawModeContext is the terminal's stdin (0) and still open
  4. For non-interactive environments, supply credentials via config/environment instead of terminal prompts

Example fix

// before
using var raw = new RawModeContext(fd); // throws when stdin is piped
// after
if (Console.IsInputRedirected || !IsATty(fd))
    throw new InvalidOperationException("Interactive terminal required; run in a TTY or use non-interactive auth.");
using var raw = new RawModeContext(fd);
Defensive patterns

Strategy: try-catch

Validate before calling

if (Console.IsInputRedirected)
    throw new InvalidOperationException("Raw terminal mode requires an interactive TTY; stdin is redirected.");

Type guard

bool CanUseRawMode(int fd) => !Console.IsInputRedirected && LinuxAnsiConsoleInput.IsATty(fd);

Try / catch

try
{
    using var raw = new RawModeContext(fd);
    // interactive prompt
}
catch (IOException ex) when (ex.Message == "Failed to read initial terminal settings.")
{
    logger.LogWarning("No TTY available; falling back to non-interactive authentication");
}

Prevention

When it happens

Trigger: Constructing RawModeContext(fd) when tcgetattr fails — fd is not a TTY (piped stdin, redirected output), fd is invalid/closed, or running in an environment without a controlling terminal.

Common situations: Running GCM's interactive prompts under CI, piping input (echo | gcm), running inside an IDE console or systemd service without a TTY, or docker run without -t.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Core/Interop/Linux/LinuxAnsiConsoleInput.cs:33

    protected override IDisposable EnterRawMode(PosixFileDescriptor fd)
    {
        return new RawModeContext(fd);
    }

    private sealed class RawModeContext : IDisposable
    {
        private readonly int _fd;
        private termios_Linux _original;
        private bool _isDisposed;

        public RawModeContext(int fd)
        {
            _fd = fd;

            if (Termios_Linux.tcgetattr(_fd, out termios_Linux t) != 0)
            {
                throw new System.IO.IOException("Failed to read initial terminal settings.");
            }

            _original = t;

            // Raw mode: disable echo, line buffering, and signal interpretation.
            // With ISIG off the terminal does not generate SIGINT, so Ctrl+C
            // arrives as a 0x03 byte that the base adapter reads and acts on
            // directly. This is robust to GCM not being the terminal's
            // foreground process group.
            //
            // TCSANOW (apply immediately) rather than TCSAFLUSH: this context
            // is entered and disposed around each keystroke read, so the
            // terminal is only raw while we are actively reading. Flushing on
            // every transition would discard typeahead still queued in the tty
            // buffer (e.g. the trailing Enter of a pasted "<down><enter>"),
            // hanging the next read.
            t.c_lflag &= ~(LocalFlags.ECHO | LocalFlags.ICANON | LocalFlags.ISIG);

View on GitHub (pinned to e8ce762cd0)