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
- Regenerate the edit against the current document contents.
- Use 0-based line numbers as the LSP protocol requires.
- Clamp the start line to len(lines)-1 when applying legacy edits.
- 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
- Always use 0-based line numbers per LSP spec.
- Recompute edits whenever the document version changes.
- Clamp legacy positions to the document length before applying.
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
- overlapping edits detected between edit %d and %d
- failed to apply edit: %w
- prompt is required
- session id missing from context
- agent message id missing from context
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/539f672971b33760.
Report an issue: GitHub.