microsoft/TypeScript · error · Error
newLength < 0
Error message
newLength < 0
What it means
Thrown by `ts.createTextChangeRange(span, newLength)` when `newLength` is negative. A `TextChangeRange` describes an edit: replace `span` with `newLength` characters; negative new length is meaningless and signals a caller bug. `textChangeRangeNewSpan` and incremental-edit plumbing route through this.
Source
Thrown at src/compiler/utilitiesPublic.ts:471
return { start, length };
}
export function createTextSpanFromBounds(start: number, end: number): TextSpan {
return createTextSpan(start, end - start);
}
export function textChangeRangeNewSpan(range: TextChangeRange): TextSpan {
return createTextSpan(range.span.start, range.newLength);
}
export function textChangeRangeIsUnchanged(range: TextChangeRange): boolean {
return textSpanIsEmpty(range.span) && range.newLength === 0;
}
export function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange {
if (newLength < 0) {
throw new Error("newLength < 0");
}
return { span, newLength };
}
export const unchangedTextChangeRange: TextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0);
/**
* Called to merge all the changes that occurred across several versions of a script snapshot
* into a single change. i.e. if a user keeps making successive edits to a script we will
* have a text change from V1 to V2, V2 to V3, ..., Vn.
*
* This function will then merge those changes into a single change range valid between V1 and
* Vn.
*/
export function collapseTextChangeRangesAcrossMultipleVersions(changes: readonly TextChangeRange[]): TextChangeRange {
if (changes.length === 0) {
return unchangedTextChangeRange;
View on GitHub (pinned to b465fdbfe1)
Solutions
- Compute `newLength` as a non-negative delta (`newText.length`, or `newEnd - newStart`).
- Validate/clamp at the boundary: `Math.max(0, newLength)`.
- For unchanged regions, use `ts.unchangedTextChangeRange` instead of constructing one.
- Unit-test the edit translator against empty, pure-insert, and pure-delete cases.
Example fix
// before const change = ts.createTextChangeRange(span, newText.end - newText.start); // can be < 0 // after const newLength = Math.max(0, newText.length); const change = ts.createTextChangeRange(span, newLength);
Defensive patterns
Strategy: validation
Validate before calling
function safeChange(span: ts.TextSpan, newLength: number): ts.TextChangeRange {
return ts.createTextChangeRange(span, Math.max(0, newLength));
} Type guard
function isValidChange(span: ts.TextSpan, newLength: number): boolean {
return span.start >= 0 && span.length >= 0 && newLength >= 0;
} Prevention
- Treat edit lengths as absolute inserted-text lengths, never as signed deltas.
- Reuse `ts.unchangedTextChangeRange` for no-op regions.
- Fuzz the edit translator with empty/insert/delete sequences.
When it happens
Trigger: Calling `createTextChangeRange` with a negative `newLength`, typically from computing `newEnd - newStart` where the new range is inverted, or from passing a negative edit length computed from an off-by-one.
Common situations: Language service/host implementations reporting incremental text changes; custom editors integrating with TS that translate their own edit format into `TextChangeRange`; snapshot differencing bugs.
Related errors
- start < 0
- length < 0
- Could not resolve JS module '${moduleName}' starting at '${i
- ${option.name} is a string value; tsconfig JSON must be pars
- tracing requires having fs\n(original error: ${e.message ||
AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12).
Data as JSON: /api/errors/8455682dc4f39361.
Report an issue: GitHub.