lima-vm/lima · error

failed to open TUI: %w; preserving guest synced workdir at %

Error message

failed to open TUI: %w; preserving guest synced workdir at %s

What it means

The interactive "Accept the changes?" menu is rendered with uiutil.Select, which requires a usable terminal. If the TUI cannot be opened (stdin/stdout not an interactive terminal despite the tty flag, terminal init failure, or interrupted input), this error is returned and the guest synced workdir is preserved at destRsyncDir.

Source

Thrown at cmd/limactl/shell.go:615

		return fmt.Errorf("failed to create temporary directory: %w; preserving guest synced workdir at %s", err, destRsyncDir)
	}
	defer func() {
		if err := os.RemoveAll(baseDir); err != nil {
			logrus.WithError(err).Warnf("Failed to clean up temporary directory %s", baseDir)
		}
	}()
	hostTmpDest := filepath.Join(baseDir, filepath.Base(hostCurrentDir))
	err = os.MkdirAll(hostTmpDest, 0o755)
	if err != nil {
		return fmt.Errorf("failed to create temporary directory: %w; preserving guest synced workdir at %s", err, destRsyncDir)
	}

	rsyncToTempDir := false

	for {
		ans, err := uiutil.Select(message, options)
		if err != nil {
			return fmt.Errorf("failed to open TUI: %w; preserving guest synced workdir at %s", err, destRsyncDir)
		}

		switch ans {
		case 0: // Yes
			return rsyncBack()
		case 1: // No
			cleanupGuestWorkdir = true
			logrus.Info("Skipping syncing back the changes to host.")
			return nil
		case 2: // View the changed contents
			var diffCmd *exec.Cmd
			if _, err := exec.LookPath("diff"); err != nil {
				logrus.WithError(err).Warn("`diff` not found; showing rsync dry-run output only")
			} else {
				diffCmd = exec.CommandContext(ctx, "diff", "-ruN", "--color=always", hostCurrentDir, hostTmpDest)
				if !rsyncToTempDir {
					paths := []string{
						remoteSource,

View on GitHub (pinned to dd909d0973)

Solutions

  1. Recover your changes from the preserved guest workdir: `limactl shell <instance>` and copy files out of destRsyncDir.
  2. Run without forced tty so the non-interactive path syncs back automatically (remove --tty / run with piped stdio).
  3. Fix the terminal environment: use a real interactive terminal, set a supported TERM (e.g. xterm-256color), enlarge the window.
  4. If in CI, avoid --tty and accept that changes sync back without prompting.

Example fix

// before
limactl shell --tty default
// after: let lima detect the terminal instead of forcing tty
limactl shell default
Defensive patterns

Strategy: fallback

Validate before calling

tty -s && tty -s <&1 && echo 'interactive terminal available' || echo 'not a real TTY: run without --tty'

Try / catch

// choose behavior based on TTY availability
if tty -s; then limactl shell default; else limactl shell default < /dev/null; fi

Prevention

When it happens

Trigger: Running `limactl shell` with `--tty` forced (or tty detection passing) but stdout/stdin is actually redirected, closed, or attached to a non-terminal (CI runner, script with pty flag, detached process); terminal too small or unsupported TERM causing the select UI to fail.

Common situations: CI systems that emulate a TTY but break interactive prompts; running limactl inside tmux/screen with odd TERM values; piping limactl output while --tty is set; window too small for the bubbletea-style prompt.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/2e451909fa31c609. Report an issue: GitHub.