{"record":{"id":"69634e2794058698","repo":"hashicorp/nomad","slug":"not-a-terminal","errorCode":null,"errorMessage":"not a terminal","messagePattern":"not a terminal","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/alloc_exec.go","lineNumber":316,"sourceCode":"\tsignalCh := make(chan os.Signal, 1)\n\tsignal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)\n\tgo func() {\n\t\tfor range signalCh {\n\t\t\tcancelFn()\n\t\t}\n\t}()\n\n\treturn client.Allocations().Exec(ctx,\n\t\talloc, task, tty, command, stdin, stdout, stderr, sizeCh, nil)\n}\n\n// setRawTerminal sets the stream terminal in raw mode, so process captures\n// Ctrl+C and other commands to forward to remote process.\n// It returns a cleanup function that restores terminal to original mode.\nfunc setRawTerminal(stream any) (cleanup func(), err error) {\n\tfd, isTerminal := term.GetFdInfo(stream)\n\tif !isTerminal {\n\t\treturn nil, errors.New(\"not a terminal\")\n\t}\n\n\tstate, err := term.SetRawTerminal(fd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn func() { term.RestoreTerminal(fd, state) }, nil\n}\n\n// setRawTerminalOutput sets the output stream in Windows to raw mode,\n// so it disables LF -> CRLF translation.\n// It's basically a no-op on unix.\nfunc setRawTerminalOutput(stream any) (cleanup func(), err error) {\n\tfd, isTerminal := term.GetFdInfo(stream)\n\tif !isTerminal {\n\t\treturn nil, errors.New(\"not a terminal\")\n\t}","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/alloc_exec.go#L298-L334","documentation":"The nomad alloc exec command puts the local terminal in raw mode so that keystrokes like Ctrl+C are forwarded to the remote process. This error is thrown by setRawTerminal when the stream passed to it (os.Stdin) is not attached to a TTY, detected via term.GetFdInfo. Without a real terminal, raw-mode capture and interactive forwarding are impossible.","triggerScenarios":"Running `nomad alloc exec` with stdin redirected (e.g. `nomad alloc exec ... < file`), piped from another command, or run from a script/CI runner/cron job where stdin is not a TTY.","commonSituations":"CI pipelines (Jenkins, GitHub Actions), SSH without -t, piping stdin from a heredoc or echo, running inside a non-interactive shell or docker exec without -t.","solutions":["Allocate a TTY: use `ssh -t` when connecting remotely, or `docker run -it` / `docker exec -it` when inside a container.","If stdin redirection is intended, add `-i` (no stdin) so the command does not try to attach stdin and enter raw mode.","Run the command from an interactive terminal session instead of a script."],"exampleFix":"// before\n$ cat input.txt | nomad alloc exec -job web /bin/sh\n// after\n$ nomad alloc exec -i -job web /bin/sh   # or run from a real TTY with an interactive shell","handlingStrategy":"validation","validationCode":"if term.IsTerminal(int(os.Stdin.Fd())) {\n    // safe to run interactive exec\n    runAllocExec()\n} else {\n    runAllocExecWithNoStdin() // add -i\n}","typeGuard":"func isTTY(f *os.File) bool { return term.IsTerminal(int(f.Fd())) }","tryCatchPattern":"if err := runAllocExec(); err != nil && strings.Contains(err.Error(), \"not a terminal\") {\n    // fall back to non-interactive mode with -i\n}","preventionTips":["Always add -i when running nomad alloc exec in scripts or CI.","Use ssh -t when relaying through SSH so a TTY exists.","Never pipe stdin into interactive exec commands."],"tags":["tty","terminal","cli","exec"],"backgroundTag":"not-a-tty","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}