wavetermdev/waveterm · error
failed to open /dev/null: %w
Error message
failed to open /dev/null: %w
What it means
After Setsid, daemonize opens /dev/null read-write and duplicates it over stdin to detach the daemon from the controlling terminal. This error wraps the os.OpenFile("/dev/null", O_RDWR, 0) failure. On Linux /dev/null is always present, so failures indicate an unusual or broken filesystem/device environment.
Source
Thrown at pkg/jobmanager/jobmanager_unix.go:28
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/wavetermdev/waveterm/pkg/wavebase"
"golang.org/x/sys/unix"
)
func daemonize(clientId string, jobId string) error {
_, err := unix.Setsid()
if err != nil {
return fmt.Errorf("failed to setsid: %w", err)
}
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
return fmt.Errorf("failed to open /dev/null: %w", err)
}
err = unix.Dup2(int(devNull.Fd()), int(os.Stdin.Fd()))
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)
}View on GitHub (pinned to a4447c1563)
Solutions
- Verify /dev/null exists and is a character device: ls -l /dev/null (expect crw-rw-rw-)
- Recreate it if broken: mknod /dev/null c 1 3 && chmod 666 /dev/null
- Mount /dev (or run with proper device cgroup allowances) in containers/chroots
- Check that the process's uid can open the node read-write before daemonizing
Example fix
// before
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
return fmt.Errorf("failed to open /dev/null: %w", err)
}
// after
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
if fi, serr := os.Stat("/dev/null"); serr != nil || !fi.Mode().IsRegular() == false {
return fmt.Errorf("/dev/null is not a proper device, fix container /dev: %w", err)
}
return fmt.Errorf("failed to open /dev/null: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
if fi, err := os.Stat("/dev/null"); err != nil {
return fmt.Errorf("/dev/null missing: mount /dev")
} else if fi.Mode()&os.ModeCharDevice == 0 {
return fmt.Errorf("/dev/null is not a character device")
} Try / catch
if err := daemonize(clientId, jobId); err != nil {
if strings.Contains(err.Error(), "failed to open /dev/null") {
log.Printf("container/chroot /dev broken: %v", errors.Unwrap(err))
}
return err
} Prevention
- Ensure /dev is mounted with a proper /dev/null node in containers and chroots
- Recreate a broken /dev/null with mknod c 1 3 and mode 666
- Verify device cgroup / sandbox policies allow opening /dev/null
- Pre-flight stat /dev/null when deploying to minimal images
When it happens
Trigger: os.OpenFile("/dev/null", os.O_RDWR, 0) fails: /dev/null missing or is not a character device, /dev not mounted (chroot/minimal container), or permission denied on the device node.
Common situations: Minimal containers or chroots without /dev mounted; /dev/null accidentally deleted or replaced by a regular file with wrong perms; extremely low fd/file permissions after privilege drop; broken device nodes in exotic sandbox environments.
Related errors
- failed to setsid: %w
- failed to dup2 stdin: %w
- failed to create log directory: %w
- failed to open log file: %w
- failed to get app directory: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/db2654cce63494a0.
Report an issue: GitHub.