ogham/exa · error

Details table options not given!

Error message

Details table options not given!

What it means

This is a Rust panic from expect() in the grid-details renderer (src/output/grid_details.rs:146). A GridDetails Render expects its DetailsOptions to carry table formatting options (details.table: Option<TableOptions>); when that field is None, find_fitting_grid() panics with 'Details table options not given!' before any output is written. The invariant exists because the grid-details view (--long --grid) draws details tables into columns, and a table with no column options cannot be built. In the shipped exa binary the mode selector (src/options/view.rs) only sets table to Some when --long is given, so the panic means a Render was constructed or reused with DetailsOptions left at its default table: None.

Source

Thrown at src/output/grid_details.rs:146

            git_ignoring:  self.git_ignoring,
            git:           self.git,
        }
    }

    // This doesn’t take an IgnoreCache even though the details one does
    // because grid-details has no tree view.

    pub fn render<W: Write>(mut self, w: &mut W) -> io::Result<()> {
        if let Some((grid, width)) = self.find_fitting_grid() {
            write!(w, "{}", grid.fit_into_columns(width))
        }
        else {
            self.give_up().render(w)
        }
    }

    pub fn find_fitting_grid(&mut self) -> Option<(grid::Grid, grid::Width)> {
        let options = self.details.table.as_ref().expect("Details table options not given!");

        let drender = self.details_for_column();

        let (first_table, _) = self.make_table(options, &drender);

        let rows = self.files.iter()
                       .map(|file| first_table.row_for_file(file, file_has_xattrs(file)))
                       .collect::<Vec<_>>();

        let file_names = self.files.iter()
                             .map(|file| self.file_style.for_file(file, self.theme).paint().promote())
                             .collect::<Vec<_>>();

        let mut last_working_grid = self.make_grid(1, options, &file_names, rows.clone(), &drender);

        if file_names.len() == 1 {
            return Some((last_working_grid, 1));
        }

View on GitHub (pinned to 3d1edbb470)

Solutions

  1. Populate the table options before rendering: construct the whole options set through exa's own deduction (options::view::Options::deduce with --long and --grid semantics) so details.table is Some(TableOptions::deduce(...)).
  2. Or set the field directly: render.details.table = Some(TableOptions { columns, ..}) with the columns you want (name, size, permissions, and so on) before calling render().
  3. If you did not mean to show details, do not use the grid-details renderer; use the plain grid renderer (Mode::Grid) which needs no table options.
  4. Longer term, replace the runtime expect with a type-level guarantee (a newtype that can only be built with table options), or fall back to self.give_up() rendering when table is None instead of panicking.

Example fix

// before
let details = DetailsOptions::default();               // table: None  -> panics in render()
let render = grid_details::Render { details: &details, /* ... */ };
render.render(&mut out)?;

// after: always pair grid-details with a table, like --long --grid does
let details = DetailsOptions {
    table: Some(TableOptions::deduce(&matches, vars)?), // or a hand-built Columns set
    ..DetailsOptions::default()
};
let render = grid_details::Render { details: &details, /* ... */ };
render.render(&mut out)?;
Defensive patterns

Strategy: validation

Validate before calling

// Before building or rendering a grid_details::Render:
use crate::output::details;

fn can_render_grid_details(details: &details::Options) -> bool {
    // grid_details::Render::find_fitting_grid() panics without table options
    details.table.is_some()
}

if !can_render_grid_details(&opts.details) {
    // choose the plain grid mode, or build the missing TableOptions first
    eprintln!("grid-details requires details table options (--long)");
}

Type guard

fn as_grid_details_ready(details: &details::Options)
    -> Option<&details::Options> {
    // Only hand the Render over when the invariant expected by
    // grid_details.rs:146 holds.
    details.table.as_ref().map(|_| details)
}

Try / catch

// Panics are not Result-based; if you must call untrusted configuration,
// isolate the render call behind catch_unwind:
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    render.render(&mut out)
}));
match result {
    Ok(Ok(())) => {}
    Ok(Err(e)) => { /* io error */ }
    Err(_panic) => {
        // 'Details table options not given!' or any other invariant panic:
        // log and fall back to the plain grid renderer
    }
}

Prevention

When it happens

Trigger: Programmatically building grid_details::Render and calling render() or find_fitting_grid() with a DetailsOptions created via its default constructor (table: None, see src/options/view.rs:117) instead of one deduced with --long (src/options/view.rs:136, TableOptions::deduce). It also fires if user code swaps/copies the DetailsOptions struct between modes, or a fork drives Mode::GridDetails rendering without matching option deduction.

Common situations: Almost exclusively hits developers embedding exa's renderer or forking the CLI who assemble Options by hand; end users of the exa binary should not reach it because --grid --long always deduces table options. Typical story: copy-pasting the Render construction from the codebase but skipping the options::view::Options::deduce step, or unit tests that construct a Render directly.

Related errors


AI-assisted analysis of ogham/exa@3d1edbb470 (2026-08-16). Data as JSON: /api/errors/7a28fd068118a98e. Report an issue: GitHub.