wavetermdev/waveterm · error

failed to read JWT token from stdin: %w

Error message

failed to read JWT token from stdin: %w

What it means

When wsh connserver is not given a JWT on the command line, askForJwtToken reads one line from stdin with fmt.Fscanln. If stdin is closed, empty, or unreadable, it wraps the failure in this error and server startup aborts.

Source

Thrown at cmd/wsh/cmd/wshcmd-connserver.go:434

	select {} // run forever
}

func askForJwtToken() (string, error) {
	// if it already exists in the environment, great, use it
	jwtToken := os.Getenv(wavebase.WaveJwtTokenVarName)
	if jwtToken != "" {
		fmt.Printf("HAVE-JWT\n")
		return jwtToken, nil
	}

	// otherwise, ask for it
	fmt.Printf("%s\n", wavebase.NeedJwtConst)

	// read a single line from stdin
	var line string
	_, err := fmt.Fscanln(os.Stdin, &line)
	if err != nil {
		return "", fmt.Errorf("failed to read JWT token from stdin: %w", err)
	}
	return strings.TrimSpace(line), nil
}

func serverRun(cmd *cobra.Command, args []string) error {
	connServerInitialEnv = envutil.PruneInitialEnv(envutil.SliceToMap(os.Environ()))

	var logFile *os.File
	if connServerDev {
		var err error
		logFilePath := fmt.Sprintf("/tmp/waveterm-connserver-%d.log", os.Getuid())
		logFile, err = os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to open log file: %v\n", err)
			log.SetFlags(log.LstdFlags | log.Lmicroseconds)
			log.SetPrefix(fmt.Sprintf("[PID:%d] ", os.Getpid()))
		} else {
			defer logFile.Close()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass the token explicitly via the --jwt flag so stdin is not needed
  2. Pipe the token in: echo "<jwt>" | wsh connserver ...
  3. Ensure the launching process provides a working stdin pipe
  4. Check that the parent process (e.g. Wave client) actually writes the token before closing the pipe

Example fix

// before
wsh connserver --conn myserver < /dev/null
// after
wsh connserver --conn myserver --jwt "$WAVETERM_JWT"
// or: echo "$WAVETERM_JWT" | wsh connserver --conn myserver
Defensive patterns

Strategy: fallback

Validate before calling

// before spawning, ensure stdin is a readable pipe/file:
fi, _ := os.Stdin.Stat()
stdinUsable := fi != nil && (fi.Mode()&os.ModeCharDevice) == 0 || fi != nil

Try / catch

line, err := readJwtFromStdin()
if err != nil {
	return fmt.Errorf("no JWT on stdin: %w (pass --jwt or pipe the token)", err)
}

Prevention

When it happens

Trigger: wsh connserver runs without --jwt and stdin is at EOF (e.g. spawned with no stdin, </dev/null, or from a daemon/service with no attached stdin), or Fscanln fails on an empty line.

Common situations: Running the command in a CI job or systemd unit where stdin is /dev/null; piping nothing into the command; launching it from a GUI/parent process that does not forward stdin; pressing Ctrl-D immediately at the prompt.

Related errors


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