{"record":{"id":"53b4fabd07948f47","repo":"argoproj/argo-workflows","slug":"cannot-convert-stdin-to-os-file-it-was-t","errorCode":null,"errorMessage":"cannot convert stdin to os.File, it was %T","messagePattern":"cannot convert stdin to os\\.File, it was %T","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/executor/osspecific/command_unix.go","lineNumber":45,"sourceCode":"\t}\n\n\tcmd.SysProcAttr = &syscall.SysProcAttr{}\n\n\tif !isTerminal(cmd.Stdin) {\n\t\t// avoid the error \"Inappropriate ioctl for device\" when\n\t\t// running in tty\n\t\t//\n\t\t// pty.Start uses setsid internally, which makes the process\n\t\t// the group leader already\n\t\tSetpgid(cmd.SysProcAttr)\n\n\t\treturn simpleStart(cmd)\n\t}\n\n\tstdin, ok := cmd.Stdin.(*os.File)\n\tif !ok {\n\t\t// should never happen when stdin is a tty\n\t\treturn nil, fmt.Errorf(\"cannot convert stdin to os.File, it was %T\", cmd.Stdin)\n\t}\n\n\tstdout := cmd.Stdout\n\tstderr := cmd.Stderr\n\n\t// pty.Start will not assign these to the pty unless they are nil\n\tcmd.Stdin = nil\n\tcmd.Stdout = nil\n\tcmd.Stderr = nil\n\n\tptmx, err := pty.Start(cmd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Handle pty size\n\tsigWinchCh := make(chan os.Signal, 1)\n\tsignal.Notify(sigWinchCh, syscall.SIGWINCH)","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/executor/osspecific/command_unix.go#L27-L63","documentation":"StartCommand uses a pty to run a command with a TTY when stdin should be interactive. pty.Start requires cmd.Stdin to be an *os.File (a real file descriptor); if the Stdin field holds any other io.Reader implementation, it cannot be attached to the pty and this error is returned. The code comment notes it 'should never happen when stdin is a tty', so it indicates a non-file stdin was configured.","triggerScenarios":"Calling StartCommand with a cmd whose Stdin was set to something other than *os.File — e.g. bytes.Buffer, os.Pipe read end is fine but a wrapped reader, strings.Reader, net.Conn, or nil-cast generic io.Reader is not.","commonSituations":"Custom executor code or tests constructing exec.Cmd with an in-memory reader and requesting a TTY/interactive start; code paths that wrap stdin in a bufio.Reader or io.LimitReader; running where the caller assumed pty.Start would fall back gracefully.","solutions":["Set cmd.Stdin to an *os.File — open /dev/null or the real terminal file: `f, _ := os.Open(\"/dev/null\"); cmd.Stdin = f`","If stdin is a pipe or wrapped reader, use simpleStart (the non-pty path) instead of the pty path","If you need to feed in-memory data over a pty, write it to an os.Pipe and pass the *os.File read end as cmd.Stdin","Remove intermediate wrappers (bufio.Reader, io.MultiReader) and pass the raw file"],"exampleFix":"// before\ncmd.Stdin = bytes.NewBufferString(input)\n// after\nr, w, _ := os.Pipe()\ngo func() { w.WriteString(input); w.Close() }()\ncmd.Stdin = r // *os.File","handlingStrategy":"type-guard","validationCode":"if _, ok := cmd.Stdin.(*os.File); !ok {\n    return simpleStart(cmd) // or replace Stdin with an *os.File\n}","typeGuard":"func isFileStdin(cmd *exec.Cmd) bool {\n    _, ok := cmd.Stdin.(*os.File)\n    return ok\n}","tryCatchPattern":"if err := StartCommand(ctx, cmd); err != nil && strings.Contains(err.Error(), \"cannot convert stdin to os.File\") {\n    return simpleStart(cmd) // fallback to non-pty start\n}","preventionTips":["Only assign *os.File values (os.Pipe ends, /dev/null, terminal fd) to cmd.Stdin","Avoid wrapping stdin in bufio.Reader / bytes.Buffer when using pty starts","Route in-memory input through os.Pipe"],"tags":["executor","pty","unix","process"],"backgroundTag":"stdin-not-a-file","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}