wavetermdev/waveterm · error

failed to create log directory: %w

Error message

failed to create log directory: %w

What it means

daemonize creates the directory for the job log file (wavebase.GetRemoteJobFilePath(jobId, "log")) with os.MkdirAll(0700) before opening it. This error wraps that MkdirAll failure. It means the job-control directory under the Wave home/sessions tree could not be created, typically a permissions or disk problem.

Source

Thrown at pkg/jobmanager/jobmanager_unix.go:40

	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)
	}
	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)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check the wrapped errno and verify the log directory path is writable by the current uid
  2. Confirm the Wave data directory (and any configured override) exists and is not read-only
  3. Free disk space if the error is ENOSPC
  4. Remove any regular file occupying the logDir path so MkdirAll can create the directory

Example fix

// before
err = os.MkdirAll(logDir, 0700)
if err != nil {
    return fmt.Errorf("failed to create log directory: %w", err)
}
// after
if err := os.MkdirAll(logDir, 0700); err != nil {
    if fi, serr := os.Lstat(logDir); serr == nil && !fi.IsDir() {
        if rmErr := os.Remove(logDir); rmErr != nil {
            return fmt.Errorf("log dir path blocked by file: %w", rmErr)
        }
        err = os.MkdirAll(logDir, 0700)
    }
    if err != nil {
        return fmt.Errorf("failed to create log directory %s: %w", logDir, err)
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

logPath := wavebase.GetRemoteJobFilePath(jobId, "log")
logDir := filepath.Dir(logPath)
if fi, err := os.Lstat(logDir); err == nil && !fi.IsDir() {
    return fmt.Errorf("%s exists and is not a directory", logDir)
}

Try / catch

if err := daemonize(clientId, jobId); err != nil {
    if strings.Contains(err.Error(), "failed to create log directory") {
        log.Printf("log dir error: %v", errors.Unwrap(err))
        // check home/data dir writability, ENOSPC, blocking file
    }
    return err
}

Prevention

When it happens

Trigger: os.MkdirAll(logDir, 0700) fails because the Wave data directory is unwritable, a path component exists as a regular file, the disk is full, or the home directory is read-only/not mounted.

Common situations: Read-only home directory or NFS mount without write access; WAVE_HOME/env override pointing at an unwritable path; leftover file where a directory is expected; ENOSPC on the data volume; SELinux denials on the home tree.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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