slint-ui/slint · info

There is always one line

Error message

There is always one line

What it means

This is an internal invariant panic from `expect("There is always one line")` in `extract_text_of_element` (tools/lsp/preview/drop_location.rs:1051). `String::split('\n')` always yields at least one item, so `lines.last()` is expected to be `Some`. The panic would only fire if `lines` were empty, which the stdlib API makes impossible; it exists to justify unwrapping the last element when computing the element's indentation fallback.

Solutions

  1. Do not filter or empty the `lines` vec between the `text.split('\n').collect()` and the `lines.last()` call.
  2. If restructuring, replace `.expect("There is always one line")` with `.unwrap_or("")` so an empty vec degrades to empty indent instead of panicking.
  3. Keep a unit test that calls extract_text_of_element with a minimal element to pin the non-empty-lines invariant.

Example fix

// before
let indent = util::find_element_indent(element).unwrap_or_else(|| {
    lines.last().expect("There is always one line").chars().take_while(|c| c.is_whitespace()).collect()
});
// after
let indent = util::find_element_indent(element).unwrap_or_else(|| {
    lines.last().map(|l| l.chars().take_while(|c| c.is_whitespace()).collect()).unwrap_or_default()
});
Defensive patterns

Strategy: fallback

Validate before calling

let lines: Vec<&str> = text.split('\n').collect();
assert!(!lines.is_empty(), "split always yields at least one line");

Type guard

fn last_line(lines: &[&str]) -> Option<&str> { lines.last().copied() }

Try / catch

// Rust: no catch; replace expect with unwrap_or at the call site
lines.last().map(|l| l.chars().take_while(|c| c.is_whitespace()).collect()).unwrap_or_default()

Prevention

When it happens

Trigger: Only reachable if the invariant 'split always returns >=1 line' were broken — e.g. if the code were refactored to filter/trim `lines` before this point, or the `text` string were replaced by an empty collection. Not triggerable from any public API with the current source.

Common situations: Developers hit this only after refactoring `extract_text_of_element` so that the `lines` vec can become empty before line 1048 (e.g. filtering out blank lines), or when reimplementing the indentation logic and dropping the `split` that guarantees a non-empty vec.

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 slint-ui/slint@bb937076de (2026-09-16). Data as JSON: /api/errors/12f3289ca910ac59. Report an issue: GitHub.

Appendix: source

Thrown at tools/lsp/preview/drop_location.rs:1051

    let mut to_delete_ranges = property_ranges(element, remove_properties);
    to_delete_ranges.sort_by(|a, b| u32::from(a.start()).cmp(&u32::from(b.start())));
    let mut offset = start_offset;
    for dr in to_delete_ranges {
        let start = usize::from(dr.start()) - offset;
        let end = usize::from(dr.end()) - offset;

        offset += end - start;

        text.drain(start..end);
    }

    // Trim leading WS to get "raw" lines
    let lines = text.split('\n').collect::<Vec<_>>();
    let indent = util::find_element_indent(element).unwrap_or_else(|| {
        lines
            .last()
            .expect("There is always one line")
            .chars()
            .take_while(|c| c.is_whitespace())
            .collect()
    });

    lines
        .iter()
        .map(|l| if l.starts_with(&indent) { l[indent.len()..].to_string() } else { l.to_string() })
        .collect::<Vec<_>>()
}

fn node_removal_text_edit(
    document_cache: &editor_preview::DocumentCache,
    node: &SyntaxNode,
    replace_with: String,
) -> Option<editor_preview::editing::SingleTextEdit> {
    let range = util::text_range_to_lsp_range(
        &node.source_file.clone(),

View on GitHub (pinned to bb937076de)