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

  1. Compute `newLength` as a non-negative delta (`newText.length`, or `newEnd - newStart`).
  2. Validate/clamp at the boundary: `Math.max(0, newLength)`.
  3. For unchanged regions, use `ts.unchangedTextChangeRange` instead of constructing one.
  4. 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

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


AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12). Data as JSON: /api/errors/8455682dc4f39361. Report an issue: GitHub.