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
- Recover your changes from the preserved guest workdir: `limactl shell <instance>` and copy files out of destRsyncDir.
- Run without forced tty so the non-interactive path syncs back automatically (remove --tty / run with piped stdio).
- Fix the terminal environment: use a real interactive terminal, set a supported TERM (e.g. xterm-256color), enlarge the window.
- 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
- Don't force --tty in scripts or CI.
- Use a real terminal with a standard TERM value (xterm-256color).
- Ensure the terminal window is large enough for interactive prompts.
- Fall back to non-interactive mode when stdin/stdout is redirected.
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
- cannot use both --tty and --yes flags at the same time
- failed to create temporary directory: %w; preserving guest s
- failed to wait for less command: %w
- could not detect a text editor binary, try setting $EDITOR
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/2e451909fa31c609.
Report an issue: GitHub.