wavetermdev/waveterm · error

cannot create ready pipe: %w

Error message

cannot create ready pipe: %w

What it means

os.Pipe() failed while creating the readiness-notification pipe pair used by the job manager child process to signal 'Wave-JobManagerStart'. This is a kernel-level fd allocation failure (EMFILE/ENFILE typically), surfaced wrapped with %w.

Source

Thrown at pkg/wshrpc/wshremote/wshremote_job.go:147

	defer impl.Lock.Unlock()
	return impl.JobManagerMap[jobId]
}

func (impl *ServerImpl) RemoteStartJobCommand(ctx context.Context, data wshrpc.CommandRemoteStartJobData) (*wshrpc.CommandStartJobRtnData, error) {
	log.Printf("RemoteStartJobCommand: starting, jobid=%s, clientid=%s\n", data.JobId, data.ClientId)
	if impl.Router == nil {
		return nil, fmt.Errorf("cannot start remote job: no router available")
	}

	wshPath, err := impl.getWshPath()
	if err != nil {
		return nil, err
	}
	log.Printf("RemoteStartJobCommand: wshPath=%s\n", wshPath)

	readyPipeRead, readyPipeWrite, err := os.Pipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create ready pipe: %w", err)
	}
	defer readyPipeRead.Close()
	defer readyPipeWrite.Close()

	cmd := exec.Command(wshPath, "jobmanager", "--jobid", data.JobId, "--clientid", data.ClientId)
	if data.PublicKeyBase64 != "" {
		cmd.Env = append(os.Environ(), "WAVETERM_PUBLICKEY="+data.PublicKeyBase64)
	}
	cmd.ExtraFiles = []*os.File{readyPipeWrite}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create stdin pipe: %w", err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create stdout pipe: %w", err)
	}
	stderr, err := cmd.StderrPipe()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check fd usage (lsof / /proc/<pid>/fd) and fix descriptor leaks
  2. Raise the soft/hard file descriptor limit (ulimit -n) or the container's nofile limit
  3. Restart the process to clear leaked descriptors

Example fix

// before
$ ulimit -n
1024
// after
$ ulimit -n 65536  # or set LimitNOFILE=65536 in the systemd unit / nofile in container runtime
Defensive patterns

Strategy: try-catch

Validate before calling

var lim syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
if lim.Cur < 1024 {
    lim.Cur = 65536
    syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim)
}

Try / catch

rtn, err := server.RemoteStartJobCommand(ctx, data)
if err != nil {
    var pe *os.PathError
    if errors.As(err, &pe) {
        // pipe allocation failure: check fd limits before retrying
    }
    return err
}

Prevention

When it happens

Trigger: Calling RemoteStartJobCommand when the process has exhausted its file descriptor limit or the OS cannot allocate pipes (fd table full, low memory).

Common situations: Long-running servers leaking file descriptors until hitting ulimit -n; systems with very low RLIMIT_NOFILE; containerized environments with small fd caps.

Related errors


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