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

Failed to enter raw terminal mode.

Error message

Failed to enter raw terminal mode.

What it means

Thrown by MacOSAnsiConsoleInput.RawModeContext when the tcsetattr() syscall fails to apply raw terminal settings (echo, canonical mode, and signal interpretation disabled) to the stdin file descriptor. The library needs raw mode to read individual keystrokes for interactive prompts; if the OS refuses the change, no interactive terminal input can be taken. This is an IOException from direct P/Invoke into macOS termios.

Solutions

  1. Ensure stdin is an interactive TTY before invoking the credential prompt; check with `tty` or isatty and fall back to non-interactive auth (credential helper store, env var, or cached OAuth token)
  2. Run the command in a real terminal session with a controlling TTY; for ssh use `ssh -t` to allocate a pseudo-terminal
  3. Redirect the interactive prompt's input: use GCM_INTERACTIVE=never / --no-interactive (or provide credentials non-interactively) so raw mode is never entered
  4. Verify the file descriptor is valid and open (fd not closed by a parent process or sandbox) before the prompt runs

Example fix

// before
git credential fill < credentials.txt   # stdin is not a TTY -> tcsetattr fails
// after
tty < /dev/tty && git credential fill   # run only when a real terminal is attached
# or force non-interactive mode
export GCM_INTERACTIVE=never
Defensive patterns

Strategy: try-catch

Validate before calling

using System;
using System.IO;

static bool StdinIsInteractive()
{
    try
    {
        // On POSIX a redirected/absent TTY cannot be put into raw mode.
        // Check via Mono.Posix / native isatty, or env heuristics:
        return !Console.IsInputRedirected && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TERM"));
    }
    catch { return false; }
}

if (!StdinIsInteractive())
{
    // skip interactive prompt; use non-interactive credential source
}

Try / catch

try
{
    // interactive prompt path that enters raw mode
    string password = promptForPassword();
}
catch (IOException ex) when (ex.Message.Contains("Failed to enter raw terminal mode"))
{
    // stdin is not a TTY — fall back to non-interactive credential lookup
}

Prevention

When it happens

Trigger: Calling EnterRawMode (via interactive prompt input paths, e.g. reading a single key) when the passed fd is not a real TTY — e.g. stdin redirected from a file or pipe, a closed/invalid fd, or running under an environment that does not own the controlling terminal.

Common situations: Running git credential prompts with stdin redirected ('git ... < file' or piped input), executing under a daemon/CI job with no controlling TTY, invoking from IDE terminals or wrappers that intercept stdin, or ssh sessions without a pseudo-terminal allocated (ssh -T / non-interactive).

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/d42a4337c6142f3d. Report an issue: GitHub.

Appendix: source

Thrown at src/Core/Interop/MacOS/MacOSAnsiConsoleInput.cs:54

            _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);

            if (Termios_MacOS.tcsetattr(_fd, SetActionFlags.TCSANOW, ref t) != 0)
            {
                throw new System.IO.IOException("Failed to enter raw terminal mode.");
            }
        }

        public void Dispose()
        {
            if (_isDisposed) return;
            Termios_MacOS.tcsetattr(_fd, SetActionFlags.TCSANOW, ref _original);
            _isDisposed = true;
        }
    }
}

View on GitHub (pinned to e8ce762cd0)