sinelaw/fresh · error

Invalid range: start offset

Error message

Invalid range: start offset {} > end offset {}

What it means

get_range resolved both DocumentPosition endpoints to byte offsets and found start_offset > end_offset, so the requested range is inverted. state.rs:2075 bails because slicing a buffer backwards is meaningless. The caller must order the positions correctly.

Solutions

  1. Order the two positions before the call (min/max on offsets)
  2. If using a selection, sort anchor and cursor so start <= end
  3. Verify the DocumentPosition values were not accidentally swapped at the call site

Example fix

// before
let text = doc.get_range(sel.cursor, sel.anchor)?;
// after
let (s, e) = if sel.cursor <= sel.anchor { (sel.cursor, sel.anchor) } else { (sel.anchor, sel.cursor) };
let text = doc.get_range(s, e)?;
Defensive patterns

Strategy: validation

Validate before calling

let (s, e) = (doc.position_to_offset(&start)?, doc.position_to_offset(&end)?);
assert!(s <= e, "range start {} after end {}", s, e);

Try / catch

match doc.get_range(start, end) {
    Err(e) if e.to_string().contains("Invalid range: start offset") => normalize_and_retry(start, end),
    other => other,
}

Prevention

When it happens

Trigger: Calling get_range with a start position that comes after the end position in the document — e.g. swapping selection anchors, or passing cursor/anchor in the wrong order.

Common situations: Selection code where the user dragged backwards (anchor after cursor); programmatic edits passing unordered offsets; LSP range conversion done in the wrong direction.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/11bb58a1c6f4cabc. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/src/state.rs:2075

                DocumentPosition::LineColumn {
                    line: pos.line,
                    column: pos.column,
                }
            } else {
                // Line index exists but metadata unavailable - fall back to byte offset
                DocumentPosition::ByteOffset(offset)
            }
        } else {
            DocumentPosition::ByteOffset(offset)
        }
    }

    fn get_range(&mut self, start: DocumentPosition, end: DocumentPosition) -> Result<String> {
        let start_offset = self.position_to_offset(start)?;
        let end_offset = self.position_to_offset(end)?;

        if start_offset > end_offset {
            anyhow::bail!(
                "Invalid range: start offset {} > end offset {}",
                start_offset,
                end_offset
            );
        }

        let bytes = self
            .buffer
            .get_text_range_mut(start_offset, end_offset - start_offset)?;

        Ok(String::from_utf8_lossy(&bytes).into_owned())
    }

    fn get_line_content(&mut self, line_number: usize) -> Option<String> {
        if !self.has_line_index() {
            return None;
        }

View on GitHub (pinned to 67894ca546)