{"record":{"id":"a8bda6134cea28ef","repo":"go-delve/delve","slug":"s-is-not-a-terminal","errorCode":null,"errorMessage":"%s is not a terminal","messagePattern":"(.+?) is not a terminal","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/native/proc_unix.go","lineNumber":20,"sourceCode":"\npackage native\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\n\tisatty \"github.com/mattn/go-isatty\"\n)\n\nfunc attachProcessToTTY(process *exec.Cmd, tty string) (*os.File, error) {\n\tf, err := os.OpenFile(tty, os.O_RDWR, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !isatty.IsTerminal(f.Fd()) {\n\t\tf.Close()\n\t\treturn nil, fmt.Errorf(\"%s is not a terminal\", f.Name())\n\t}\n\tprocess.Stdin = f\n\tprocess.Stdout = f\n\tprocess.Stderr = f\n\tprocess.SysProcAttr.Setpgid = false\n\tprocess.SysProcAttr.Setsid = true\n\tprocess.SysProcAttr.Setctty = true\n\n\treturn f, nil\n}\n","sourceCodeStart":2,"sourceCodeEnd":31,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/native/proc_unix.go#L2-L31","documentation":"attachProcessToTTY opens the given TTY path for read-write to use as stdin/stdout/stderr of a spawned process. After opening it verifies with isatty.IsTerminal that the file is actually a terminal; if not, it closes the handle and returns this error. It exists because Delve needs a real terminal device for process I/O redirection, not a regular file, pipe, or socket.","triggerScenarios":"Calling the attach/launch path that redirects process I/O to a TTY (attachProcessToTTY) with a path that opens successfully but is not a terminal device — e.g. a regular file, FIFO, /dev/null, or a socket.","commonSituations":"Passing a log file or output redirection target where a TTY path is expected; using a pty slave path that was closed or replaced; running under environments that provide pipes instead of terminal devices (CI runners, docker without -t); misconfigured `--tty` style options pointing at non-terminal files.","solutions":["Verify the path passed is a real terminal device (e.g. /dev/pts/N, /dev/tty*), not a regular file or pipe.","Check the path with `isatty`/`stat -c %F` before passing it: `ls -l <path>` should show it as a character-special terminal device.","If running in Docker/CI, allocate a pseudo-terminal (docker run -t, `script -q /dev/null`) so the process gets a valid TTY.","Ensure the TTY was not closed or reassigned between obtaining the path and calling the API; re-acquire a fresh TTY path."],"exampleFix":"// before\nproc, err := attachProcessToTTY(\"/tmp/app.log\")\n// after\nf, _ := os.OpenFile(\"/tmp/app.log\", os.O_WRONLY, 0)\nif isatty.IsTerminal(f.Fd()) {\n    proc, err = attachProcessToTTY(\"/tmp/app.log\")\n} else {\n    proc, err = attachProcessToTTY(\"/dev/pts/3\") // real terminal\n}","handlingStrategy":"validation","validationCode":"f, err := os.OpenFile(ttyPath, os.O_RDWR, 0)\nif err != nil { return err }\ndefer f.Close()\nif !isatty.IsTerminal(f.Fd()) {\n    return fmt.Errorf(\"%s is not a terminal\", ttyPath)\n}","typeGuard":"func isTTY(f *os.File) bool { return isatty.IsTerminal(f.Fd()) }","tryCatchPattern":null,"preventionTips":["Always stat/verify the path is a character device before passing it as a TTY.","In CI/Docker use `docker run -t` or `script -q -c ... /dev/null` to allocate a pty.","Never pass regular files or pipes where a terminal is required."],"tags":["terminal","tty","linux","process-launch"],"backgroundTag":"not-a-terminal","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}