wavetermdev/waveterm · error

cannot write job auth token: %w

Error message

cannot write job auth token: %w

What it means

After the jobmanager process starts, RemoteStartJobCommand writes the 'Wave-JobAccessToken:<token>' line to its stdin pipe. A failed write means the pipe is broken — the child has already exited or closed stdin — and the process is killed before returning the wrapped error.

Source

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

	if err != nil {
		return nil, fmt.Errorf("cannot create stdout pipe: %w", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create stderr pipe: %w", err)
	}
	log.Printf("RemoteStartJobCommand: created pipes\n")

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("cannot start job manager: %w", err)
	}
	readyPipeWrite.Close()
	log.Printf("RemoteStartJobCommand: job manager process started\n")

	jobAuthTokenLine := fmt.Sprintf("Wave-JobAccessToken:%s\n", data.JobAuthToken)
	if _, err := stdin.Write([]byte(jobAuthTokenLine)); err != nil {
		cmd.Process.Kill()
		return nil, fmt.Errorf("cannot write job auth token: %w", err)
	}
	stdin.Close()
	log.Printf("RemoteStartJobCommand: wrote auth token to stdin\n")

	go func() {
		scanner := bufio.NewScanner(stderr)
		for scanner.Scan() {
			line := scanner.Text()
			log.Printf("RemoteStartJobCommand: stderr: %s\n", line)
		}
		if err := scanner.Err(); err != nil {
			log.Printf("RemoteStartJobCommand: error reading stderr: %v\n", err)
		} else {
			log.Printf("RemoteStartJobCommand: stderr EOF\n")
		}
	}()

	go func() {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check the captured stderr goroutine logs for the child's actual exit reason
  2. Verify the remote wsh binary version matches the server (re-run 'wsh init' to update)
  3. Run the resolved wsh binary manually with 'jobmanager --jobid ... --clientid ...' to see its immediate error
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: ensure the jobmanager subcommand exists
out, err := exec.Command(wshPath, "jobmanager", "--help").CombinedOutput()
if err != nil {
    return fmt.Errorf("jobmanager subcommand unavailable: %v: %s", err, out)
}

Try / catch

rtn, err := server.RemoteStartJobCommand(ctx, data)
if err != nil {
    if strings.Contains(err.Error(), "cannot write job auth token") {
        // child died before reading stdin; check stderr logs and wsh version
    }
    return err
}

Prevention

When it happens

Trigger: The wsh jobmanager child crashes/exits immediately after Start() (bad flags, missing config, panic), causing the stdin pipe to break when the auth token is written.

Common situations: Incompatible wsh binary version on the remote that rejects the jobmanager subcommand; child dying from a missing dependency or configuration; EPIPE on the stdin pipe.

Related errors


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