wavetermdev/waveterm · error

unknown controller type %q

Error message

unknown controller type %q

What it means

Returned when ShellController.ControllerType is neither the controller type handled by createCmdStrAndOpts nor any known branch in setupAndStartShellProcess. It is an internal invariant violation: the block's controller was created with an unrecognized type value.

Source

Thrown at pkg/blockcontroller/shellcontroller.go:421

		cmdOpts.Interactive = true
		cmdOpts.Login = true
		cmdOpts.Cwd = blockMeta.GetString(waveobj.MetaKey_CmdCwd, "")
		if cmdOpts.Cwd != "" {
			cwdPath, err := wavebase.ExpandHomeDir(cmdOpts.Cwd)
			if err != nil {
				return nil, err
			}
			cmdOpts.Cwd = cwdPath
		}
	} else if bc.ControllerType == BlockController_Cmd {
		var cmdOptsPtr *shellexec.CommandOptsType
		cmdStr, cmdOptsPtr, err = createCmdStrAndOpts(bc.BlockId, blockMeta, remoteName)
		if err != nil {
			return nil, err
		}
		cmdOpts = *cmdOptsPtr
	} else {
		return nil, fmt.Errorf("unknown controller type %q", bc.ControllerType)
	}
	var shellProc *shellexec.ShellProc
	swapToken := makeSwapToken(ctx, logCtx, bc.BlockId, blockMeta, remoteName, connUnion.ShellType)
	cmdOpts.SwapToken = swapToken
	blocklogger.Debugf(logCtx, "[conndebug] created swaptoken: %s\n", swapToken.Token)
	if connUnion.ConnType == ConnType_Wsl {
		wslConn := connUnion.WslConn
		if !connUnion.WshEnabled {
			shellProc, err = shellexec.StartWslShellProcNoWsh(ctx, rc.TermSize, cmdStr, cmdOpts, wslConn)
			if err != nil {
				return nil, err
			}
		} else {
			sockName := wslConn.GetDomainSocketName()
			rpcContext := wshrpc.RpcContext{
				ProcRoute: true,
				SockName:  sockName,
				BlockId:   bc.BlockId,

View on GitHub (pinned to a4447c1563)

Solutions

  1. Delete and re-create the terminal block so a fresh ShellController is built with a valid ControllerType
  2. Upgrade/downgrade so app version matches the workspace/session data version
  3. Inspect the saved block state and correct ControllerType to a supported value
  4. Report as a bug if a stock terminal block reproduces this — it indicates state corruption

Example fix

// before: stale restored block with unknown ControllerType reused as-is
shellProc, err := bc.setupAndStartShellProcess(logCtx, rc, blockMeta)

// after: recreate the block controller if type is unknown
if !isKnownControllerType(bc.ControllerType) {
    controller = controllerRegistry.Recreate(bc.BlockId)
}
Defensive patterns

Strategy: type-guard

Type guard

func knownControllerType(t ControllerType) bool {
    switch t {
    case ControllerType_Shell:
        return true
    default:
        return false
    }
}
// guard: if !knownControllerType(bc.ControllerType) { recreate block controller }

Try / catch

proc, err := bc.setupAndStartShellProcess(logCtx, rc, blockMeta)
if err != nil && strings.Contains(err.Error(), "unknown controller type") {
    // recreate the block/controller and retry once
}

Prevention

When it happens

Trigger: DoRunShellCommand invoked on a ShellController whose bc.ControllerType holds an unexpected/legacy/empty value so the if/else chain falls through to the final else.

Common situations: Session data restored from an older Wave version with a deprecated controller type; corrupted block metadata in the workspace state; a plugin or experimental build writing a custom ControllerType.

Related errors


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