{"record":{"id":"b2d129c16f7fe712","repo":"docker/cli","slug":"unable-to-set-io-streams-as-raw-terminal-s","errorCode":null,"errorMessage":"unable to set IO streams as raw terminal: %s","messagePattern":"unable to set IO streams as raw terminal: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/container/hijack.go","lineNumber":105,"sourceCode":"\tcase err := <-detached:\n\t\t// Got a detach key sequence.\n\t\treturn err\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\t}\n}\n\nfunc (h *hijackedIOStreamer) setupInput() (restore func(), _ error) {\n\tif h.inputStream == nil || !h.tty {\n\t\t// No need to setup input TTY.\n\t\t// The restore func is a nop.\n\t\treturn func() {}, nil\n\t}\n\tif err := validateDetachKeys(h.detachKeys); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := setRawTerminal(h.streams); err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to set IO streams as raw terminal: %s\", err)\n\t}\n\n\t// Use sync.Once so we may call restore multiple times but ensure we\n\t// only restore the terminal once.\n\trestore = sync.OnceFunc(func() {\n\t\t_ = restoreTerminal(h.streams, h.inputStream)\n\t})\n\n\t// Wrap the input to detect detach escape sequence.\n\t// Use default escape keys if an invalid sequence is given.\n\tescapeKeys := defaultEscapeKeys\n\tif h.detachKeys != \"\" {\n\t\tvar err error\n\t\tescapeKeys, err = term.ToBytes(h.detachKeys)\n\t\tif err != nil {\n\t\t\trestore()\n\t\t\treturn nil, err\n\t\t}","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/command/container/hijack.go#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (stdin is piped, no real TTY)\necho hi | docker run -it --rm alpine\n\n# after (real TTY, or drop -t)\ndocker run -i --rm alpine","handlingStrategy":"validation","validationCode":"import \"github.com/moby/term\"\nif !term.IsTerminal(os.Stdin.Fd()) {\n    return errors.New(\"cannot use raw mode: stdin is not a terminal\")\n}","typeGuard":"func isInteractiveTTY(f *os.File) bool {\n    return term.IsTerminal(f.Fd())\n}","tryCatchPattern":"// Degrade gracefully: if raw mode is unavailable, drop -t and run non-interactive.\nif err != nil && strings.Contains(err.Error(), \"raw terminal\") {\n    cmd.Args = removeFlag(cmd.Args, \"-t\"); /* rerun */\n}","preventionTips":["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."],"tags":["container","terminal","tty","streaming","hijack"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}