iced-rs/iced · error

Caches must not be empty

Error message

Caches must not be empty

What it means

The syntect-backed Highlighter keeps parser snapshots (ParseState, ScopeStack) every LINES_PER_SNAPSHOT = 50 lines in caches: Vec<...>. highlight_line asserts with caches.last().expect("Caches must not be empty") (highlighter/src/lib.rs:89) that at least one snapshot exists. The stock lifecycle (new, update, change_line) always keeps the vec non-empty - change_line truncates then pushes - so this panic signals a broken invariant: a bug in the iced/highlighter version in use or a hand-written Highlighter trait implementation driving the editor.

Solutions

  1. Use only the stock lifecycle: Highlighter::new(Settings), update, change_line, highlight_line
  2. If you implement the Highlighter trait yourself, guarantee caches always holds at least one entry before any highlight_line call
  3. Call change_line(line) whenever highlighting jumps to a new region instead of only letting current_line advance
  4. If hit with unmodified iced, upgrade iced/iced_highlighter and report the panic with a reproducer

Example fix

// before - custom Highlighter impl with empty caches
fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_> {
    let (parser, stack) = self.caches.last().expect("Caches must not be empty");
    // ...
}

// after - keep the invariant: always seed one snapshot
fn new(settings: &Settings) -> Self {
    Self {
        caches: vec![(ParseState::new(syntax), ScopeStack::new())],
        current_line: 0,
        // ...
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// when highlighting jumps to a new region, resync snapshots first
if target_line / 50 != highlighter.current_line() / 50 {
    highlighter.change_line(target_line);
}
highlighter.highlight_line(line_text);

Prevention

When it happens

Trigger: highlight_line called when the caches vec is empty - not producible via Highlighter::new/update/change_line; the line-89 expect also fires when current_line has advanced past the available snapshots (current_line / 50 >= caches.len()) while caches is somehow empty, e.g. a custom trait impl that cleared it or a fork with an off-by-one in change_line.

Common situations: Custom core::text::highlighter::Highlighter implementations that manage snapshots themselves; older/forked iced versions with change_line bugs; driving highlight_line directly in tests or fuzzing outside the editor widget lifecycle.

Related errors


AI-assisted analysis of iced-rs/iced@3de451447b (2026-08-17). Data as JSON: /api/errors/456b1cb313a077a0. Report an issue: GitHub.

Appendix: source

Thrown at highlighter/src/lib.rs:89

            self.current_line = snapshot * LINES_PER_SNAPSHOT;
        } else {
            self.caches.truncate(1);
            self.current_line = 0;
        }

        let (parser, stack) = self.caches.last().cloned().unwrap_or_else(|| {
            (
                parsing::ParseState::new(self.syntax),
                parsing::ScopeStack::new(),
            )
        });

        self.caches.push((parser, stack));
    }

    fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_> {
        if self.current_line / LINES_PER_SNAPSHOT >= self.caches.len() {
            let (parser, stack) = self.caches.last().expect("Caches must not be empty");

            self.caches.push((parser.clone(), stack.clone()));
        }

        self.current_line += 1;

        let (parser, stack) = self.caches.last_mut().expect("Caches must not be empty");

        let ops = parser.parse_line(line, &SYNTAXES).unwrap_or_default();

        Box::new(scope_iterator(ops, line, stack, &self.highlighter))
    }

    fn current_line(&self) -> usize {
        self.current_line
    }
}

View on GitHub (pinned to 3de451447b)