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
- 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(...)).
- 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().
- 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.
- 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
- Always derive view options through exa's own options::view::Options::deduce with --long and --grid instead of struct literals; it guarantees table: Some(...).
- Treat details.table = None as 'details view not configured'; never feed such Options into grid_details::Render.
- If you copy Render construction examples from the codebase, copy the matching Options construction from src/options/view.rs:117-136 as well.
- Add a unit test that asserts opts.details.table.is_some() before every code path that renders Mode::GridDetails.
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.