wavetermdev/waveterm · critical

failed to daemonize: %w

Error message

failed to daemonize: %w

What it means

After signaling readiness via the readyFile, SetupJobManager calls daemonize to detach the job manager into a background daemon process. This error wraps any failure from daemonize (fork/exec/session setup failure), meaning the job manager could not run detached.

Source

Thrown at pkg/jobmanager/jobmanager.go:79

	}
	err = MakeJobDomainSocket(clientId, jobId)
	if err != nil {
		return err
	}

	go func() {
		defer func() {
			panichandler.PanicHandler("JobManager:processInputQueue", recover())
		}()
		WshCmdJobManager.processInputQueue()
	}()

	fmt.Fprintf(readyFile, JobManagerStartLabel+"\n")
	readyFile.Close()

	err = daemonize(clientId, jobId)
	if err != nil {
		return fmt.Errorf("failed to daemonize: %w", err)
	}

	go func() {
		defer func() {
			panichandler.PanicHandler("JobManager:keepalive", recover())
		}()
		ticker := time.NewTicker(1 * time.Hour)
		defer ticker.Stop()
		for range ticker.C {
			log.Printf("keepalive: job manager active\n")
		}
	}()

	return nil
}

func (jm *JobManager) processInputQueue() {
	for data := range jm.InputQueue.C() {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Inspect the wrapped error from daemonize for the concrete syscall failure (setsid vs exec vs fork)
  2. Clear stale job domain sockets/lock files from a previous crashed run and retry
  3. Run in an environment permitting process creation (check ulimit -u, seccomp/apparmor policies)
  4. Verify the wsh executable path is valid and the binary can re-exec itself

Example fix

// before
err = daemonize(clientId, jobId)
// after
err = daemonize(clientId, jobId)
if err != nil {
    log.Printf("daemonize failed: %v; running in foreground instead", err)
    // fall back to foreground run or surface a clear user-facing message
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: can we create processes and the socket path?
if err := unix.Setsid(); !errors.Is(err, unix.EPERM) == false {
    return fmt.Errorf("sandbox forbids setsid; daemonize will fail")
}
os.RemoveAll(jobSocketPath(clientId, jobId)) // clear stale sockets

Try / catch

if err := jobmanager.SetupJobManager(cid, jid, pub, tok, rf); err != nil {
    if strings.Contains(err.Error(), "failed to daemonize") {
        log.Printf("daemonize failed: %v; falling back to foreground mode", err)
        // run foreground or surface sandbox hint to user
        return
    }
    return err
}

Prevention

When it happens

Trigger: daemonize's underlying exec/re-exec fails — binary path not resolvable, fork resource limits hit, setsid failure, or the re-executed process cannot re-open the domain socket path.

Common situations: Running inside restricted containers/sandboxes that forbid setsid or double-fork (some seccomp profiles); low process/fd limits; readonly /tmp or socket path collisions from a stale previous run.

Related errors


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