emilk/egui · error

LayoutJob has no sections

Error message

LayoutJob has no sections

What it means

LayoutJob::format_at_byte resolves the TextFormat in effect at a byte offset by finding the section covering that range; it calls sections.last().expect("LayoutJob has no sections") purely as a fallback for indexes past the end. If the job has zero sections the expect panics — the documented precondition is that the job was built with at least one LayoutSection via LayoutJob::append.

Solutions

  1. Only call format_at_byte when !job.sections.is_empty(); check first and supply a default TextFormat otherwise
  2. Ensure every LayoutJob gets at least one append(...) call — even for empty text, append a zero-length default section
  3. Replace the expect with a graceful return of a default TextFormat when sections is empty
  4. If the job comes from library code that should never be empty, file/log the job's section count at the construction site to find where sections were dropped

Example fix

// before
let format = job.format_at_byte(ByteIndex(42));
// after
let format = if job.sections.is_empty() {
    &TextFormat::default()
} else {
    job.format_at_byte(ByteIndex(42))
};
Defensive patterns

Strategy: validation

Validate before calling

debug_assert!(!job.sections.is_empty(), "format_at_byte called on a LayoutJob with no sections");

Prevention

When it happens

Trigger: Calling format_at_byte on a LayoutJob constructed empty (LayoutJob::default() or new()) or on a job whose sections were cleared/drained before layout; calling it during layout of a job where every append path was skipped (empty input string with no explicit default section).

Common situations: Custom text widgets that query per-byte formats on user-built jobs; rendering an empty string by appending no sections and then asking for formatting; refactors that build sections conditionally and end up with an empty vec; jobs deserialized or cached in an older format losing their sections.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of emilk/egui@441971a776 (2026-09-12). Data as JSON: /api/errors/47722846cc17ea3e. Report an issue: GitHub.

Appendix: source

Thrown at crates/epaint/src/text/text_layout_types.rs:230

            return;
        }

        self.sections.push(LayoutSection {
            leading_space,
            byte_range,
            format,
        });
    }

    /// The [`TextFormat`] of the section containing the character starting at the given byte index.
    ///
    /// If the index is past the end, the format of the last section is returned.
    ///
    /// Panics if the job has no sections.
    /// Assumes [`LayoutJob::sections`] are ordered by increasing `byte_range` (as produced by [`Self::append`]).
    pub fn format_at_byte(&self, byte_idx: ByteIndex) -> &TextFormat {
        self.debug_sanity_check();
        let last = self.sections.last().expect("LayoutJob has no sections");
        let idx = self
            .sections
            .partition_point(|section| section.byte_range.end <= byte_idx);
        let section = self.sections.get(idx).unwrap_or(last);
        &section.format
    }

    /// Check the [`Self::sections`] invariant: the sections are ordered and together
    /// cover the whole of [`Self::text`] with no gaps and no overlaps.
    ///
    /// Only does anything in debug builds.
    #[cfg_attr(not(debug_assertions), expect(clippy::unused_self))]
    pub fn debug_sanity_check(&self) {
        #[cfg(debug_assertions)]
        {
            if self.sections.is_empty() {
                assert!(
                    self.text.is_empty(),

View on GitHub (pinned to 441971a776)