wavetermdev/waveterm · error

SwapToken is required in CommandOptsType

Error message

SwapToken is required in CommandOptsType

What it means

StartWslShellProc requires cmdOpts.SwapToken to be non-nil because the WSL session bootstrap exchanges a swap token with the remote wsh server. If the token is missing it returns "SwapToken is required in CommandOptsType" instead of starting a session that could not be authenticated/handed off.

Source

Thrown at pkg/shellexec/shellexec.go:178

	if termSize.Rows == 0 || termSize.Cols == 0 {
		termSize.Rows = shellutil.DefaultTermRows
		termSize.Cols = shellutil.DefaultTermCols
	}
	if termSize.Rows <= 0 || termSize.Cols <= 0 {
		return nil, fmt.Errorf("invalid term size: %v", termSize)
	}
	cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
	if err != nil {
		return nil, err
	}
	cmdWrap := MakeCmdWrap(ecmd, cmdPty, true)
	return &ShellProc{Cmd: cmdWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
}

func StartWslShellProc(ctx context.Context, termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *wslconn.WslConn) (*ShellProc, error) {
	if cmdOpts.SwapToken == nil {
		return nil, fmt.Errorf("SwapToken is required in CommandOptsType")
	}
	client := conn.GetClient()
	conn.Infof(ctx, "WSL-NEWSESSION (StartWslShellProc)")
	connRoute := wshutil.MakeConnectionRouteId(conn.GetName())
	rpcClient := wshclient.GetBareRpcClient()
	remoteInfo, err := wshclient.RemoteGetInfoCommand(rpcClient, &wshrpc.RpcOpts{Route: connRoute, Timeout: 2000})
	if err != nil {
		return nil, fmt.Errorf("unable to obtain client info: %w", err)
	}
	log.Printf("client info collected: %+#v", remoteInfo)
	var shellPath string
	if cmdOpts.ShellPath != "" {
		conn.Infof(ctx, "using shell path from command opts: %s\n", cmdOpts.ShellPath)
		shellPath = cmdOpts.ShellPath
	}
	configShellPath := conn.GetConfigShellPath()
	if shellPath == "" && configShellPath != "" {
		conn.Infof(ctx, "using shell path from config (conn:shellpath): %s\n", configShellPath)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Generate and set cmdOpts.SwapToken before calling StartWslShellProc (use the same token generation the WSH setup path uses)
  2. Route WSL connections through the correct setup path that populates SwapToken
  3. If a no-wsh session is intended, call StartWslShellProcNoWsh instead

Example fix

// before
var cmdOpts shellexec.CommandOptsType // SwapToken nil
proc, err := shellexec.StartWslShellProc(ctx, termSize, cmdStr, cmdOpts, conn)
// after
cmdOpts.SwapToken = &swapToken // generate via the wsh swap-token helper
proc, err := shellexec.StartWslShellProc(ctx, termSize, cmdStr, cmdOpts, conn)
Defensive patterns

Strategy: validation

Validate before calling

if cmdOpts.SwapToken == nil {
    return fmt.Errorf("cannot start WSL shell: SwapToken not set")
}
proc, err := shellexec.StartWslShellProc(ctx, termSize, cmdStr, cmdOpts, conn)

Type guard

func hasSwapToken(o shellexec.CommandOptsType) bool {
    return o.SwapToken != nil
}

Try / catch

proc, err := shellexec.StartWslShellProc(ctx, termSize, cmdStr, cmdOpts, conn)
if err != nil && err.Error() == "SwapToken is required in CommandOptsType" {
    // caller bug: fix setup path, not retryable
    return err
}

Prevention

When it happens

Trigger: Calling StartWslShellProc with a CommandOptsType whose SwapToken field is nil — i.e. the caller did not generate the secret used by the wsh swap handshake for WSL connections.

Common situations: Code path intended for local/SSH shells reused for WSL without setting SwapToken; a refactor that dropped swap-token generation in setupAndStartShellProcess.

Understand the failure class

Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.

Related errors


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