docker/cli · error
unable to set IO streams as raw terminal
Error message
unable to set IO streams as raw terminal: %s
What it means
Returned by setupInput (hijack.go:105) when setRawTerminal(h.streams) fails while switching the terminal into raw mode for an interactive hijacked session. Raw mode is required so that key sequences (including the detach keys) pass through unmodified.
Solutions
- Run from a real interactive terminal; do not pipe stdin when using -t.
- Drop the -t flag if you do not need a TTY (e.g. -i only).
- Verify the terminal is allocated; in CI use a pty wrapper if interactivity is required.
Example fix
# before (stdin is piped, no real TTY) echo hi | docker run -it --rm alpine # after (real TTY, or drop -t) docker run -i --rm alpine
Defensive patterns
Strategy: validation
Validate before calling
import "github.com/moby/term"
if !term.IsTerminal(os.Stdin.Fd()) {
return errors.New("cannot use raw mode: stdin is not a terminal")
} Type guard
func isInteractiveTTY(f *os.File) bool {
return term.IsTerminal(f.Fd())
} Try / catch
// Degrade gracefully: if raw mode is unavailable, drop -t and run non-interactive.
if err != nil && strings.Contains(err.Error(), "raw terminal") {
cmd.Args = removeFlag(cmd.Args, "-t"); /* rerun */
} Prevention
- Only pass -t when stdin is a real terminal.
- In CI/automation, omit -t or allocate a pty.
- Test interactive commands in a real shell.
When it happens
Trigger: The terminal cannot be placed into raw mode: stdin is not a TTY (piped), the terminal device does not support termios raw mode, or the platform terminal library returns an error.
Common situations: Running an interactive `docker run -it`/`attach`/`exec` with stdin redirected from a pipe or file, running under an environment without a controlling terminal (some CI), or a terminal that rejects raw mode.
Related errors
- unable to setup input stream
- invalid detach keys ( )
- timeout waiting for stats
- cannot attach to a stopped container, start it first
- cannot attach to a paused container, unpause it first
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/b2d129c16f7fe712.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/container/hijack.go:105
case err := <-detached:
// Got a detach key sequence.
return err
case <-ctx.Done():
return ctx.Err()
}
}
func (h *hijackedIOStreamer) setupInput() (restore func(), _ error) {
if h.inputStream == nil || !h.tty {
// No need to setup input TTY.
// The restore func is a nop.
return func() {}, nil
}
if err := validateDetachKeys(h.detachKeys); err != nil {
return nil, err
}
if err := setRawTerminal(h.streams); err != nil {
return nil, fmt.Errorf("unable to set IO streams as raw terminal: %s", err)
}
// Use sync.Once so we may call restore multiple times but ensure we
// only restore the terminal once.
restore = sync.OnceFunc(func() {
_ = restoreTerminal(h.streams, h.inputStream)
})
// Wrap the input to detect detach escape sequence.
// Use default escape keys if an invalid sequence is given.
escapeKeys := defaultEscapeKeys
if h.detachKeys != "" {
var err error
escapeKeys, err = term.ToBytes(h.detachKeys)
if err != nil {
restore()
return nil, err
}View on GitHub (pinned to 4f84911bfe)