{"record":{"id":"8455682dc4f39361","repo":"microsoft/TypeScript","slug":"newlength-0","errorCode":null,"errorMessage":"newLength < 0","messagePattern":"newLength < 0","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/compiler/utilitiesPublic.ts","lineNumber":471,"sourceCode":"\r\n    return { start, length };\r\n}\r\n\r\nexport function createTextSpanFromBounds(start: number, end: number): TextSpan {\r\n    return createTextSpan(start, end - start);\r\n}\r\n\r\nexport function textChangeRangeNewSpan(range: TextChangeRange): TextSpan {\r\n    return createTextSpan(range.span.start, range.newLength);\r\n}\r\n\r\nexport function textChangeRangeIsUnchanged(range: TextChangeRange): boolean {\r\n    return textSpanIsEmpty(range.span) && range.newLength === 0;\r\n}\r\n\r\nexport function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange {\r\n    if (newLength < 0) {\r\n        throw new Error(\"newLength < 0\");\r\n    }\r\n\r\n    return { span, newLength };\r\n}\r\n\r\nexport const unchangedTextChangeRange: TextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0);\r\n\r\n/**\r\n * Called to merge all the changes that occurred across several versions of a script snapshot\r\n * into a single change.  i.e. if a user keeps making successive edits to a script we will\r\n * have a text change from V1 to V2, V2 to V3, ..., Vn.\r\n *\r\n * This function will then merge those changes into a single change range valid between V1 and\r\n * Vn.\r\n */\r\nexport function collapseTextChangeRangesAcrossMultipleVersions(changes: readonly TextChangeRange[]): TextChangeRange {\r\n    if (changes.length === 0) {\r\n        return unchangedTextChangeRange;\r","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/microsoft/TypeScript/blob/b465fdbfe175304d9b977da137b2c178ae1091d3/src/compiler/utilitiesPublic.ts#L453-L489","documentation":"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.","triggerScenarios":"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.","commonSituations":"Language service/host implementations reporting incremental text changes; custom editors integrating with TS that translate their own edit format into `TextChangeRange`; snapshot differencing bugs.","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."],"exampleFix":"// before\nconst change = ts.createTextChangeRange(span, newText.end - newText.start); // can be < 0\n// after\nconst newLength = Math.max(0, newText.length);\nconst change = ts.createTextChangeRange(span, newLength);","handlingStrategy":"validation","validationCode":"function safeChange(span: ts.TextSpan, newLength: number): ts.TextChangeRange {\n  return ts.createTextChangeRange(span, Math.max(0, newLength));\n}","typeGuard":"function isValidChange(span: ts.TextSpan, newLength: number): boolean {\n  return span.start >= 0 && span.length >= 0 && newLength >= 0;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["textspan","textchangerange","compiler-api","internal-invariant","incremental"],"backgroundTag":null,"analyzedSha":"b465fdbfe175304d9b977da137b2c178ae1091d3","analyzedAt":"2026-08-12T05:38:42.698Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}