{"record":{"id":"0256d7971bab46a6","repo":"docker/cli","slug":"cannot-attach-stdin-to-a-tty-enabled-container-bec","errorCode":null,"errorMessage":"cannot attach stdin to a TTY-enabled container because stdin is not a terminal","messagePattern":"cannot attach stdin to a TTY-enabled container because stdin is not a terminal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/streams/in.go","lineNumber":74,"sourceCode":"// value.\nfunc (i *In) SetRawTerminal() error {\n\treturn i.cs.setRawTerminal(term.SetRawTerminal)\n}\n\n// RestoreTerminal restores the terminal state if SetRawTerminal succeeded earlier.\nfunc (i *In) RestoreTerminal() {\n\ti.cs.restoreTerminal()\n}\n\n// CheckTty reports an error when stdin is requested for a TTY-enabled\n// container, but the client stdin is not itself a terminal (for example,\n// when input is piped or redirected).\nfunc (i *In) CheckTty(attachStdin, ttyMode bool) error {\n\t// TODO(thaJeztah): consider inlining this code and deprecating the method.\n\tif !ttyMode || !attachStdin || i.cs.isTerminal() {\n\t\treturn nil\n\t}\n\treturn errors.New(\"cannot attach stdin to a TTY-enabled container because stdin is not a terminal\")\n}\n\n// SetIsTerminal overrides whether a terminal is connected. It is used to\n// override this property in unit-tests, and should not be depended on for\n// other purposes.\nfunc (i *In) SetIsTerminal(isTerminal bool) {\n\ti.cs.setIsTerminal(isTerminal)\n}\n","sourceCodeStart":56,"sourceCodeEnd":83,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/streams/in.go#L56-L83","documentation":"Returned by In.CheckTty when a container is launched with TTY mode (-t) and stdin attached (-i), but the client process's stdin is not a real terminal (e.g. piped or redirected). Raw-mode terminal setup requires an actual TTY, so Docker refuses to start to avoid silent garbled I/O.","triggerScenarios":"Running 'echo data | docker run -it ...', 'docker run -it ... < file', or embedding docker in a CI/cron job where stdin is a pipe — i.e. attachStdin and ttyMode are both true but i.cs.isTerminal() is false.","commonSituations":"Piping input into an interactive container in CI pipelines; wrapping docker run in a script that redirects stdin; forgetting that -it implies an interactive TTY the host cannot provide.","solutions":["Drop the -t (tty) flag when stdin is piped: 'echo data | docker run -i ...'.","If a TTY is genuinely required, run the command from an actual interactive shell or allocate a PTY wrapper (e.g. script, python pty).","Separate concerns: use 'docker run -i' for piped input and reserve '-it' for human-interactive sessions."],"exampleFix":"# before: echo hello | docker run -it alpine\n# after:  echo hello | docker run -i alpine","handlingStrategy":"validation","validationCode":"// Check whether stdin is a terminal before requesting -it with piped input\nfunc shouldUseTTY(attachStdin bool, in *streams.In) bool {\n\tif !attachStdin { return false }\n\treturn in.IsTerminal()\n}\n// Usage: only add \"-t\" when shouldUseTTY(true, dockerCLI.In()) is true.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use -i (not -it) when stdin is piped or redirected.","In Go code, call In.CheckTty(attachStdin, ttyMode) early and handle the error explicitly.","Reserve -it for genuinely interactive sessions in a real terminal."],"tags":["streams","tty","stdin","container-run"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}