{"record":{"id":"315afa84b2ce0ecf","repo":"wavetermdev/waveterm","slug":"failed-to-dup2-stdout-w","errorCode":null,"errorMessage":"failed to dup2 stdout: %w","messagePattern":"failed to dup2 stdout: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobmanager/jobmanager_unix.go","lineNumber":49,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dup2 stdin: %w\", err)\n\t}\n\tdevNull.Close()\n\n\tlogPath := wavebase.GetRemoteJobFilePath(jobId, \"log\")\n\tlogDir := filepath.Dir(logPath)\n\terr = os.MkdirAll(logDir, 0700)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create log directory: %w\", err)\n\t}\n\n\tlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open log file: %w\", err)\n\t}\n\terr = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dup2 stdout: %w\", err)\n\t}\n\terr = unix.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dup2 stderr: %w\", err)\n\t}\n\n\tlog.SetOutput(logFile)\n\tlog.Printf(\"job manager daemonized, logging to %s\\n\", logPath)\n\tlog.Printf(\"job owner clientid: %s\\n\", clientId)\n\n\tsignal.Ignore(syscall.SIGHUP)\n\n\treturn nil\n}\n","sourceCodeStart":31,"sourceCodeEnd":64,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobmanager/jobmanager_unix.go#L31-L64","documentation":"This error is returned by the Unix daemonize function in the job manager when unix.Dup2 fails to redirect the process's stdout file descriptor to the opened log file. The job manager redirects stdout and stderr to a log file so daemonized output is captured. Dup2 failing here is an OS-level file descriptor error (EBADF, EMFILE, etc.).","triggerScenarios":"daemonize() is called and unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd())) returns a non-nil error — e.g. the process has no valid stdout descriptor (detached/no TTY with fd 0-2 closed) or fd exhaustion.","commonSituations":"Launching the job manager from a supervisor that closed file descriptors, running under environments where stdout is closed, or hitting the open file descriptor limit (ulimit -n) after opening the log file.","solutions":["Ensure file descriptors 0/1/2 are open before daemonizing (open /dev/null or a pipe for them if a supervisor closed them).","Check ulimit -n / rlimit NOFILE for fd exhaustion and raise it.","Retry daemonize after fixing the environment; check the wrapped %w error for the exact syscall errno."],"exampleFix":"// before\nerr = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))\n// after\n// ensure stdout exists even when the process was launched with fd 1 closed\nif os.Stdout.Fd() < 0 {\n    devnull, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)\n    unix.Dup2(int(devnull.Fd()), 1)\n}\nerr = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))","handlingStrategy":"validation","validationCode":"for fd := 0; fd <= 2; fd++ {\n    if _, err := unix.FcntlInt(uintptr(fd), unix.F_GETFD, 0); err != nil {\n        devnull, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)\n        unix.Dup2(int(devnull.Fd()), fd)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := daemonize(clientId, jobId); err != nil {\n    var errno unix.Errno\n    if errors.As(err, &errno) { log.Printf(\"dup2 errno: %v\", errno) }\n    return fmt.Errorf(\"daemonize failed: %w\", err)\n}","preventionTips":["Keep fds 0-2 open in process launchers/supervisors","Raise RLIMIT_NOFILE before spawning the job manager","Log the wrapped errno to distinguish EBADF vs EMFILE"],"tags":["unix","file-descriptor","daemonization","syscall"],"backgroundTag":"dup2-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}