slint-ui/slint · error

line_limit_cut returns an existing line index

Error message

line_limit_cut returns an existing line index

What it means

In the shared Parley text layout, the `line_limit_cut` bookkeeping (the paragraph/line index where text was cut by a line-limit) is expected to always refer to an existing line in the paragraph's laid-out lines. `lines().nth(last_line).expect(...)` panics if the recorded index is stale — i.e. the internal invariant between the cut computation and the final parley layout broke.

Solutions

  1. Reproduce with a minimal .slint file and file an upstream bug (this is an internal invariant failure)
  2. Update/patch Slint — the fix is usually in the text layout's line-limit recomputation
  3. As a workaround, avoid combining line limits with dynamic font/size changes until fixed
Defensive patterns

Strategy: fallback

Try / catch

// internal panic, not caller-recoverable; only a global guard helps
std::panic::catch_unwind(AssertUnwindSafe(|| draw_text(...))).unwrap_or_else(|_| default_text_render());

Prevention

When it happens

Trigger: Rendering a Text element with a line limit (max height / `wrap` with vertical truncation) through the Parley-based (femtovg/skia) renderer when the cached cut index no longer matches the relaid-out paragraph lines.

Common situations: Text elements with `overflow: elide`/height-constrained text under dynamic font loading, font-size changes, or resize races where the layout is recomputed but the cut index is not; generally indicates a runtime bug rather than user error.

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/1ef2da8eaa8d3bd5. Report an issue: GitHub.

Appendix: source

Thrown at internal/core/textlayout/sharedparley/layout.rs:221

                    .map(|line| {
                        let metrics = line.metrics();
                        PhysicalLength::new(metrics.inline_min_coord + metrics.advance)
                    })
                    .fold(PhysicalLength::zero(), PhysicalLength::max),
                _ => PhysicalLength::new(p.layout.full_width()),
            }
        })
        .fold(PhysicalLength::zero(), PhysicalLength::max);
    // With an active line limit, the height only extends to the bottom of the last kept line, so
    // that the preferred height and vertical alignment are based on what is actually shown.
    let height = match line_limit_cut {
        Some((last_paragraph, last_line)) => {
            let para = &paragraphs[last_paragraph];
            let line = para
                .layout
                .lines()
                .nth(last_line)
                .expect("line_limit_cut returns an existing line index");
            para.y + PhysicalLength::new(line.metrics().block_max_coord)
        }
        None => paragraphs
            .last()
            .map_or(PhysicalLength::zero(), |p| p.y + PhysicalLength::new(p.layout.height())),
    };

    let y_offset = vertical_offset(max_physical_height, options.vertical_align, height);

    Layout {
        paragraphs,
        y_offset,
        elision_info,
        max_width,
        height,
        max_physical_height,
        line_limit_cut,
        line_breaking_inputs: inputs,

View on GitHub (pinned to bb937076de)