nikivdev/code · error

failed to draw env setup UI: {err}

Error message

failed to draw env setup UI: {err}

What it means

The env setup TUI (`run_app`, invoked by `run_env_setup`) wraps every `terminal.draw(|f| draw_ui(f, app))` call; if the backend draw fails (ratatui/termion/crossterm backend error), it is converted into this anyhow error naming the UI. This typically indicates the terminal backend cannot render — not a problem with the setup logic itself.

Source

Thrown at src/env_setup.rs:242

            .unwrap_or_else(|| "production".to_string());

        self.result = Some(EnvSetupResult {
            env_file: self.env_file_path(),
            environment,
            selected_keys,
            apply: self.apply,
        });
    }
}

fn run_app<B: ratatui::backend::Backend>(
    terminal: &mut Terminal<B>,
    app: &mut EnvSetupApp,
) -> Result<Option<EnvSetupResult>> {
    loop {
        terminal
            .draw(|f| draw_ui(f, app))
            .map_err(|err| anyhow::anyhow!("failed to draw env setup UI: {err}"))?;

        if event::poll(std::time::Duration::from_millis(200))? {
            if let CEvent::Key(key) = event::read()? {
                if handle_key(app, key)? {
                    return Ok(app.result.take());
                }
            }
        }
    }
}

fn handle_key(app: &mut EnvSetupApp, key: KeyEvent) -> Result<bool> {
    match key.code {
        KeyCode::Char('q') => return Ok(true),
        KeyCode::Esc => return Ok(step_back(app)),
        _ => {}
    }

View on GitHub (pinned to a747e741ae)

Solutions

  1. Run `f env setup` in a real interactive terminal (no pipes/redirects on stdout)
  2. Check TERM is set to a supported value and the PTY is healthy (reconnect SSH)
  3. Use non-interactive env configuration (flags/config file) if no TTY is available
  4. Read the wrapped backend error in the message for the specific terminal-backend failure

Example fix

// before
f env setup | tee setup.log
// after
f env setup   # run directly in an interactive terminal; log via `script` if needed
Defensive patterns

Strategy: try-catch

Validate before calling

if !std::io::stdout().is_terminal() {
    eprintln!("env setup requires an interactive terminal");
    std::process::exit(2);
}

Try / catch

match run_app(terminal, app) {
    Err(e) if e.to_string().contains("failed to draw env setup UI") => {
        eprintln!("TUI unavailable ({e}); use non-interactive setup flags instead.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: `f env setup` starts the interactive TUI and `Terminal::draw` returns an error — e.g. output redirected to a non-terminal/pipe, backend I/O failure, unsupported terminal, or a broken/closed stdout during redraws inside the event loop.

Common situations: Running the setup wizard in CI or via `| tee` where stdout is not a TTY; SSH sessions with broken PTY; terminals that reject required escape sequences; very old/unusual TERM settings.


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/21d96d54fdb00a6c. Report an issue: GitHub.