Skip to content

fix(tui): fleet setup panics (index out of bounds) when model filter matches nothing #5953

Description

@7jrxt42BxFZo4iAnN4CX

Summary

In the Fleet setup wizard, typing a model/provider filter that matches zero routes crashes the TUI with index out of bounds: the len is 0 but the index is 0.

Reproduce

  1. /fleet setup (or flow "ready-made fleet + custom") → advance to the model selection step.
  2. Press / to enable the type-to-filter (model_filter_active).
  3. Type a query that matches nothing, e.g. minimax followed by a space (or any non-existent provider/model).
  4. The wizard panics. Repeated twice in the same session.

Crash (from logs):

Process panicked at crates/tui/src/tui/views/fleet_setup.rs:2391:19:
index out of bounds: the len is 0 but the index is 0
thread 'codewhale-main' panicked at ... fleet_setup.rs:2391:19

Root cause

render_choice_step indexes the detail pane unconditionally:

// fleet_setup.rs:2391
let choice = &choices[selected.min(choices.len().saturating_sub(1))];

When choices is empty, choices.len().saturating_sub(1) is 0, selected.min(0) is 0, so choices[0] panics. The saturating_sub(1) guard does not protect against len == 0.

The Step::Model renderer builds filtered_choices from the typed query and always calls render_choice_step with it:

// fleet_setup.rs (~1876)
let filtered_choices: Vec<Choice> = filtered.iter().map(...).collect();
...
render_choice_step(chunks[1], buf, &filtered_choices, selected, &context);

With a no-match query, filtered_choices is empty and the crash occurs.

(Regression introduced by commit 48f1414e40 "tui(fleet): type to find a route instead of arrowing past all of them" — the new filter path.)

Proposed fix

Guard against an empty choices in render_choice_step — render an empty-state row (e.g. No routes match "<query>") and skip the detail pane instead of indexing. For example:

// detail: if there is nothing to show, render a "no matches" state and return
let Some(choice) = choices.get(selected.min(choices.len().saturating_sub(1))) else {
    // draw the empty list / message; no detail pane
    return;
};

(or equivalently early-return in the Step::Model caller when filtered_choices.is_empty()).

Acceptance criteria

  • Typing a filter with no matches in the Fleet setup model step shows a "no matches" state and never panics.
  • Navigating up/down with an empty filter result is safe (no index access).
  • A filter that matches rows still works as before.

Environment

  • codewhale 0.9.12 (dev), Linux, tmux. Reproducible on current main.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    • Status
      In progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions