gitbutlerapp/gitbutler · error

FuzzyPickerItem::columns must return a searchable column

Error message

FuzzyPickerItem::columns must return a searchable column

What it means

filter_items in the TUI fuzzy picker calls FuzzyPickerItem::columns(SearchableToken(())) and requires that at least one returned column has a searchable token. If an item implementation returns no column marked searchable, `.find(...)` yields None and the `.expect(...)` panics. This is an internal contract between the FuzzyPickerItem trait and the picker's filtering logic: every item must expose at least one searchable column so the matcher can score it.

Solutions

  1. Open the FuzzyPickerItem implementation for the displayed item type and ensure at least one column returned has `searchable: Some(...)`
  2. Change the code to fall back gracefully instead of panicking: use `.find(...).ok()?` / `filter_map` to skip non-searchable items
  3. Add a debug_assert or unit test per item type asserting columns() yields a searchable column

Example fix

// before
let col = item
    .columns(SearchableToken(()))
    .into_iter()
    .find(|col| col.searchable.is_some())
    .expect("FuzzyPickerItem::columns must return a searchable column");
// after
let col = item
    .columns(SearchableToken(()))
    .into_iter()
    .find(|col| col.searchable.is_some())?;
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust: verify an item is searchable before adding it to the picker
fn assert_searchable(item: &dyn FuzzyPickerItem) {
    debug_assert!(
        item.columns(SearchableToken(())).iter().any(|c| c.searchable.is_some()),
        "FuzzyPickerItem must return at least one searchable column"
    );
}

Type guard

fn has_searchable_column(item: &dyn FuzzyPickerItem) -> bool {
    item.columns(SearchableToken(())).iter().any(|c| c.searchable.is_some())
}

Try / catch

// Prefer skipping unfilterable items over panicking:
let col = match item.columns(SearchableToken(())).into_iter().find(|c| c.searchable.is_some()) {
    Some(c) => c,
    None => return None, // in filter_map
};

Prevention

When it happens

Trigger: Pressing a key that triggers fuzzy filtering in the `but` status TUI while any item in the picker violates the trait contract — i.e. its `columns()` implementation returns no column with `searchable: Some(_)`.

Common situations: Adding a new FuzzyPickerItem variant (or modifying columns()) and forgetting to tag at least one column as searchable; a refactor changes column construction so `searchable` becomes None; a column list becomes empty for a particular data state.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/c935a3d2dec9ae48. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/legacy/status/tui/fuzzy_picker.rs:271

        if query.is_empty() {
            self.items_to_show.extend(
                self.items
                    .iter()
                    .enumerate()
                    .map(|(item_idx, _)| ItemToShow::Plain { item_idx }),
            );
        } else {
            let mut fuzzy_matches = self
                .items
                .iter()
                .enumerate()
                .filter_map(|(item_idx, item)| {
                    let col = item
                        .columns(SearchableToken(()))
                        .into_iter()
                        .find(|col| col.searchable.is_some())
                        .expect("FuzzyPickerItem::columns must return a searchable column");
                    let (score, indices) = self.matcher.fuzzy_indices(&col.text, query)?;
                    Some((item_idx, col, score, indices))
                })
                .collect::<Vec<_>>();
            fuzzy_matches.sort_unstable_by(|(_, _, score_a, _), (_, _, score_b, _)| {
                score_a.cmp(score_b).reverse()
            });
            self.items_to_show.extend(fuzzy_matches.into_iter().map(
                |(item_idx, _, _, indices)| ItemToShow::FuzzyMatch {
                    item_idx,
                    char_indices: indices,
                },
            ));
        }
    }

    pub fn handle_message(
        mut self,

View on GitHub (pinned to 58e5313667)