wavetermdev/waveterm · error

failed to dup2 stderr: %w

Error message

failed to dup2 stderr: %w

What it means

This error is returned by daemonize when unix.Dup2 fails while redirecting stderr (fd 2) to the log file, after stdout redirection already succeeded. It wraps the underlying syscall errno. Same class of OS-level descriptor problems as the stdout variant.

Source

Thrown at pkg/jobmanager/jobmanager_unix.go:53

	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

  1. Ensure fd 2 is open before daemonizing (open /dev/null onto fd 2 if needed).
  2. Raise the file descriptor limit (ulimit -n / systemd LimitNOFILE).
  3. Inspect the wrapped errno from the %w error to pinpoint the syscall failure.

Example fix

// before
err = unix.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
    return fmt.Errorf("failed to dup2 stderr: %w", err)
}
// after
// guarantee fd 2 is valid before dup2
if _, err := unix.FcntlInt(2, unix.F_GETFD, 0); err != nil {
    devnull, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)
    unix.Dup2(int(devnull.Fd()), 2)
}
err = unix.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
Defensive patterns

Strategy: validation

Validate before calling

if _, err := unix.FcntlInt(2, unix.F_GETFD, 0); err != nil {
    devnull, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)
    unix.Dup2(int(devnull.Fd()), 2)
}

Try / catch

if err := daemonize(clientId, jobId); err != nil {
    if strings.Contains(err.Error(), "dup2 stderr") {
        log.Printf("stderr redirect failed: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: daemonize() succeeds opening the log file and redirecting stdout, but unix.Dup2(int(logFile.Fd()), int(os.Stderr.Fd())) returns an error — typically because fd 2 is closed/invalid or fds are exhausted.

Common situations: Process started with stderr closed (e.g. `cmd 2>&-`), supervisor frameworks that close fd 2, or rlimit NOFILE exhaustion.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/cdbb8b33a6fba202. Report an issue: GitHub.