{"record":{"id":"47722846cc17ea3e","repo":"emilk/egui","slug":"layoutjob-has-no-sections","errorCode":null,"errorMessage":"LayoutJob has no sections","messagePattern":"LayoutJob has no sections","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/epaint/src/text/text_layout_types.rs","lineNumber":230,"sourceCode":"            return;\n        }\n\n        self.sections.push(LayoutSection {\n            leading_space,\n            byte_range,\n            format,\n        });\n    }\n\n    /// The [`TextFormat`] of the section containing the character starting at the given byte index.\n    ///\n    /// If the index is past the end, the format of the last section is returned.\n    ///\n    /// Panics if the job has no sections.\n    /// Assumes [`LayoutJob::sections`] are ordered by increasing `byte_range` (as produced by [`Self::append`]).\n    pub fn format_at_byte(&self, byte_idx: ByteIndex) -> &TextFormat {\n        self.debug_sanity_check();\n        let last = self.sections.last().expect(\"LayoutJob has no sections\");\n        let idx = self\n            .sections\n            .partition_point(|section| section.byte_range.end <= byte_idx);\n        let section = self.sections.get(idx).unwrap_or(last);\n        &section.format\n    }\n\n    /// Check the [`Self::sections`] invariant: the sections are ordered and together\n    /// cover the whole of [`Self::text`] with no gaps and no overlaps.\n    ///\n    /// Only does anything in debug builds.\n    #[cfg_attr(not(debug_assertions), expect(clippy::unused_self))]\n    pub fn debug_sanity_check(&self) {\n        #[cfg(debug_assertions)]\n        {\n            if self.sections.is_empty() {\n                assert!(\n                    self.text.is_empty(),","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/emilk/egui/blob/441971a776322a482e371775219380eca812cfa9/crates/epaint/src/text/text_layout_types.rs#L212-L248","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Only call format_at_byte when !job.sections.is_empty(); check first and supply a default TextFormat otherwise","Ensure every LayoutJob gets at least one append(...) call — even for empty text, append a zero-length default section","Replace the expect with a graceful return of a default TextFormat when sections is empty","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"],"exampleFix":"// before\nlet format = job.format_at_byte(ByteIndex(42));\n// after\nlet format = if job.sections.is_empty() {\n    &TextFormat::default()\n} else {\n    job.format_at_byte(ByteIndex(42))\n};","handlingStrategy":"validation","validationCode":"debug_assert!(!job.sections.is_empty(), \"format_at_byte called on a LayoutJob with no sections\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always append at least one section via LayoutJob::append, even for empty strings (zero-length default section)","Guard custom widgets: check job.sections.is_empty() before per-byte format queries","After deserializing or mutating a LayoutJob, assert it still has sections","Return a default TextFormat for empty jobs in wrapper helpers instead of delegating blindly"],"tags":["text","layout","panic","empty-collection","egui"],"backgroundTag":"empty-required-field","analyzedSha":"441971a776322a482e371775219380eca812cfa9","analyzedAt":"2026-09-12T04:29:21.500Z","contentChangedAt":"2026-09-12T04:29:21.500Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}