docker/cli · error

cannot attach stdin to a TTY-enabled container because…

Error message

cannot attach stdin to a TTY-enabled container because stdin is not a terminal

What it means

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.

Solutions

  1. Drop the -t (tty) flag when stdin is piped: 'echo data | docker run -i ...'.
  2. If a TTY is genuinely required, run the command from an actual interactive shell or allocate a PTY wrapper (e.g. script, python pty).
  3. Separate concerns: use 'docker run -i' for piped input and reserve '-it' for human-interactive sessions.

Example fix

# before: echo hello | docker run -it alpine
# after:  echo hello | docker run -i alpine
Defensive patterns

Strategy: validation

Validate before calling

// Check whether stdin is a terminal before requesting -it with piped input
func shouldUseTTY(attachStdin bool, in *streams.In) bool {
	if !attachStdin { return false }
	return in.IsTerminal()
}
// Usage: only add "-t" when shouldUseTTY(true, dockerCLI.In()) is true.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/0256d7971bab46a6. Report an issue: GitHub.

Appendix: source

Thrown at cli/streams/in.go:74

// value.
func (i *In) SetRawTerminal() error {
	return i.cs.setRawTerminal(term.SetRawTerminal)
}

// RestoreTerminal restores the terminal state if SetRawTerminal succeeded earlier.
func (i *In) RestoreTerminal() {
	i.cs.restoreTerminal()
}

// CheckTty reports an error when stdin is requested for a TTY-enabled
// container, but the client stdin is not itself a terminal (for example,
// when input is piped or redirected).
func (i *In) CheckTty(attachStdin, ttyMode bool) error {
	// TODO(thaJeztah): consider inlining this code and deprecating the method.
	if !ttyMode || !attachStdin || i.cs.isTerminal() {
		return nil
	}
	return errors.New("cannot attach stdin to a TTY-enabled container because stdin is not a terminal")
}

// SetIsTerminal overrides whether a terminal is connected. It is used to
// override this property in unit-tests, and should not be depended on for
// other purposes.
func (i *In) SetIsTerminal(isTerminal bool) {
	i.cs.setIsTerminal(isTerminal)
}

View on GitHub (pinned to 4f84911bfe)