charmbracelet/crush · error

invalid start line: %d

Error message

invalid start line: %d

What it means

applyTextEdit validates that the edit's start line falls within the split document lines before indexing. A start line that is negative or >= len(lines) is rejected with this message. This is a guard against stale or malformed LSP positions.

Source

Thrown at internal/lsp/util/edit.go:95

	// Only add a newline if the original file had one and we haven't already added it
	if endsWithNewline && !strings.HasSuffix(newContent.String(), lineEnding) {
		newContent.WriteString(lineEnding)
	}

	if err := os.WriteFile(path, []byte(newContent.String()), 0o644); err != nil {
		return fmt.Errorf("failed to write file: %w", err)
	}

	return nil
}

func applyTextEdit(lines []string, edit protocol.TextEdit, encoding powernap.OffsetEncoding) ([]string, error) {
	startLine := int(edit.Range.Start.Line)
	endLine := int(edit.Range.End.Line)

	// Validate positions before accessing lines.
	if startLine < 0 || startLine >= len(lines) {
		return nil, fmt.Errorf("invalid start line: %d", startLine)
	}
	if endLine < 0 || endLine >= len(lines) {
		endLine = len(lines) - 1
	}

	var startChar, endChar int
	switch encoding {
	case powernap.UTF8:
		// UTF-8: Character offset is already a byte offset
		startChar = int(edit.Range.Start.Character)
		endChar = int(edit.Range.End.Character)
	case powernap.UTF16:
		// UTF-16 (default): Convert to byte offset
		startLineContent := lines[startLine]
		endLineContent := lines[endLine]
		startChar = powernap.PositionToByteOffset(startLineContent, edit.Range.Start.Character)
		endChar = powernap.PositionToByteOffset(endLineContent, edit.Range.End.Character)
	default:

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Regenerate the edit against the current document contents.
  2. Use 0-based line numbers as the LSP protocol requires.
  3. Clamp the start line to len(lines)-1 when applying legacy edits.
  4. Validate the range against the document length before constructing the TextEdit.

Example fix

// before
startLine := 1 // 1-based, document has 1 line
// after
startLine := 0 // LSP uses 0-based lines
Defensive patterns

Strategy: validation

Validate before calling

if int(edit.Range.Start.Line) < 0 || int(edit.Range.Start.Line) >= len(strings.Split(content, "\n")) {
    return fmt.Errorf("start line %d out of range", edit.Range.Start.Line)
}

Try / catch

if strings.Contains(err.Error(), "invalid start line") {
    // rebuild edits from current document snapshot and retry
}

Prevention

When it happens

Trigger: An edit whose Range.Start.Line is out of bounds for the current document (empty file with line > 0, positions from a longer previous version, or 1-based vs 0-based line confusion).

Common situations: Edit computed against a document that has since shrunk or been emptied; manually constructed edits using 1-based line numbers; tool-generated edits after a bulk deletion.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/539f672971b33760. Report an issue: GitHub.