wavetermdev/waveterm · critical

failed to daemonize

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.

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.

Appendix: 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)