{"record":{"id":"0f990f9fe11e3b17","repo":"git-ecosystem/git-credential-manager","slug":"failed-to-open-ttydevicename-for-reading","errorCode":null,"errorMessage":"Failed to open {TtyDeviceName} for reading.","messagePattern":"Failed to open (.+?) for reading\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/Core/Interop/Posix/PosixAnsiConsoleInput.cs","lineNumber":94,"sourceCode":"\n    // Raw mode is reference-counted: every keystroke read and every interactive\n    // session (see BeginRawModeSession) takes a hold, and the terminal is only\n    // returned to cooked mode once the last hold is released. Holding raw mode\n    // across a whole prompt — rather than re-entering it per keystroke — stops\n    // the driver echoing fast-typed characters in the cooked window that would\n    // otherwise open between reads. Guarded by _rawModeLock.\n    private int _rawDepth;\n    private IDisposable _rawMode;\n\n    protected PosixAnsiConsoleInput()\n    {\n        PlatformUtils.EnsurePosix();\n\n        _fd = new PosixFileDescriptor(TtyDeviceName, OpenFlags.O_RDWR);\n        if (_fd.IsInvalid)\n        {\n            _fd.Dispose();\n            throw new System.IO.IOException($\"Failed to open {TtyDeviceName} for reading.\");\n        }\n\n        // The terminal is left in its normal (cooked) mode except during an\n        // active keystroke read (see ReadKey). That is what keeps Ctrl+C working\n        // when GCM is doing other work — MSAL polling, a GUI, the network — and\n        // not just while a Spectre prompt happens to be reading: in cooked mode\n        // the terminal driver raises SIGINT and the runtime's default\n        // disposition terminates the process.\n        //\n        // These handlers exist to restore the terminal if the process is killed\n        // by a signal *while* a read is holding raw mode; the runtime does not\n        // unwind our finally on a fatal signal, so without them the user's\n        // terminal would be left in raw mode. SIGINT is included: although a\n        // Ctrl+C keypress during a read cannot raise it (ISIG is off — Ctrl+C\n        // arrives as a 0x03 byte we turn into an InterruptedException), an\n        // external SIGINT (kill -INT, or one delivered to the foreground\n        // process group) can still arrive mid-read. The handler restores only\n        // when a read is active and otherwise no-ops, letting the runtime's","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/Core/Interop/Posix/PosixAnsiConsoleInput.cs#L76-L112","documentation":"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.","triggerScenarios":"Constructing PosixAnsiConsoleInput when TtyDeviceName (typically /dev/tty) cannot be opened read-write: no controlling terminal, or permission denied on the device node.","commonSituations":"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.","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."],"exampleFix":"// before (CI, no TTY)\n$ git push   # GCM tries to prompt -> IOException\n// after\n$ export GCM_INTERACTIVE=never\n$ git config credential.credentialStore cache  # or run in an interactive shell","handlingStrategy":"try-catch","validationCode":"bool HasTty() => File.Exists(\"/dev/tty\") &&\n    SysCall.isatty(SysCall.open(\"/dev/tty\", OpenFlags.O_RDWR));","typeGuard":null,"tryCatchPattern":"try { using var input = new PosixAnsiConsoleInput(); ... }\ncatch (IOException ex) when (ex.Message.Contains(\"Failed to open\"))\n{ // no controlling terminal: use non-interactive flow }\nreturn ExitNonInteractiveFallback();","preventionTips":["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."],"tags":["posix","tty","io","terminal"],"backgroundTag":"file-open-failed","analyzedSha":"e8ce762cd04b4100ae637b5fbf39ef9d0a96561e","analyzedAt":"2026-09-11T17:15:08.753Z","contentChangedAt":"2026-09-11T17:15:08.753Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}