wavetermdev/waveterm · error
failed to dup2 stdout: %w
Error message
failed to dup2 stdout: %w
What it means
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.).
Source
Thrown at pkg/jobmanager/jobmanager_unix.go:49
if err != nil {
return fmt.Errorf("failed to dup2 stdin: %w", err)
}
devNull.Close()
logPath := wavebase.GetRemoteJobFilePath(jobId, "log")
logDir := filepath.Dir(logPath)
err = os.MkdirAll(logDir, 0700)
if err != nil {
return fmt.Errorf("failed to create log directory: %w", err)
}
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
return fmt.Errorf("failed to open log file: %w", err)
}
err = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
if err != nil {
return fmt.Errorf("failed to dup2 stdout: %w", err)
}
err = unix.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
return fmt.Errorf("failed to dup2 stderr: %w", err)
}
log.SetOutput(logFile)
log.Printf("job manager daemonized, logging to %s\n", logPath)
log.Printf("job owner clientid: %s\n", clientId)
signal.Ignore(syscall.SIGHUP)
return nil
}
View on GitHub (pinned to a4447c1563)
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.
Example fix
// before
err = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
// after
// ensure stdout exists even when the process was launched with fd 1 closed
if os.Stdout.Fd() < 0 {
devnull, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)
unix.Dup2(int(devnull.Fd()), 1)
}
err = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd())) Defensive patterns
Strategy: validation
Validate before calling
for fd := 0; fd <= 2; fd++ {
if _, err := unix.FcntlInt(uintptr(fd), unix.F_GETFD, 0); err != nil {
devnull, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)
unix.Dup2(int(devnull.Fd()), fd)
}
} Try / catch
if err := daemonize(clientId, jobId); err != nil {
var errno unix.Errno
if errors.As(err, &errno) { log.Printf("dup2 errno: %v", errno) }
return fmt.Errorf("daemonize failed: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to dup2 stderr: %w
- failed to dup2 stdin: %w
- failed to get pgid: %w
- invalid pgid returned: %d
- failed to daemonize: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/315afa84b2ce0ecf.
Report an issue: GitHub.