{"record":{"id":"dbd9abd04efe1f09","repo":"microsoft/TypeScript","slug":"start-0","errorCode":null,"errorMessage":"start < 0","messagePattern":"start < 0","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/compiler/utilitiesPublic.ts","lineNumber":448,"sourceCode":"    while (i < spans.length) {\r\n        let span = spans[i];\r\n        let j = i + 1;\r\n        while (j < spans.length && textSpanIntersectsWithTextSpan(span, spans[j])) {\r\n            const start = Math.min(span.start, spans[j].start);\r\n            const end = Math.max(textSpanEnd(span), textSpanEnd(spans[j]));\r\n            span = createTextSpanFromBounds(start, end);\r\n            j++;\r\n        }\r\n        i = j;\r\n        result.push(span);\r\n    }\r\n\r\n    return result;\r\n}\r\n\r\nexport function createTextSpan(start: number, length: number): TextSpan {\r\n    if (start < 0) {\r\n        throw new Error(\"start < 0\");\r\n    }\r\n    if (length < 0) {\r\n        throw new Error(\"length < 0\");\r\n    }\r\n\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","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/microsoft/TypeScript/blob/b465fdbfe175304d9b977da137b2c178ae1091d3/src/compiler/utilitiesPublic.ts#L430-L466","documentation":"Thrown by `ts.createTextSpan(start, length)` when `start` is negative. `TextSpan` models a half-open range into source text; a negative start is always a bug in the caller (offset computation underflow). `createTextSpanFromBounds` delegates here via `createTextSpan(start, end - start)`, so out-of-order bounds also surface as this error.","triggerScenarios":"Calling `createTextSpan` with `start < 0`, or `createTextSpanFromBounds(start, end)` where `start` is negative — typically from subtracting offsets, parsing an empty/malformed node, or off-by-one position math in a transform/language-service plugin.","commonSituations":"Custom transforms that compute spans from node positions; language-service plugins mishandling end-of-file offsets; converting external 1-based line/column to 0-based absolute positions incorrectly.","solutions":["Clamp `start` to `Math.max(0, computed)` before calling.","Audit the upstream position math (it should be producing a non-negative absolute offset).","Use `ts.createTextSpanFromBounds` with verified `start <= end` ordering.","Add an assertion/log of `start`/`length` at the call site during development."],"exampleFix":"// before\nconst span = ts.createTextSpan(node.end - node.getStart(), len); // can be < 0\n// after\nconst start = Math.max(0, node.end - node.getStart());\nconst span = ts.createTextSpan(start, Math.max(0, len));","handlingStrategy":"validation","validationCode":"function safeSpan(start: number, length: number): ts.TextSpan {\n  if (start < 0 || length < 0) return ts.createTextSpan(0, 0);\n  return ts.createTextSpan(start, length);\n}","typeGuard":"function isValidSpanStart(start: number): boolean { return Number.isInteger(start) && start >= 0; }","tryCatchPattern":null,"preventionTips":["Always derive spans from already-validated node `pos`/`end`, never from arithmetic that can underflow.","Clamp external positions to `[0, fileLength]` before constructing spans."],"tags":["textspan","compiler-api","internal-invariant","position"],"backgroundTag":null,"analyzedSha":"b465fdbfe175304d9b977da137b2c178ae1091d3","analyzedAt":"2026-08-12T05:38:42.698Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}