{"record":{"id":"8ccc7addf4d5af6d","repo":"oraios/serena","slug":"symbol-range-end-line-self-end-line-col-self","errorCode":null,"errorMessage":"Symbol range end (line {self._end_line}, col {self._end_col}) is out of bounds for a file with {len(self._lines)} lines","messagePattern":"Symbol range end \\(line (.+?), col (.+?)\\) is out of bounds for a file with (.+?) lines","errorType":"validation","errorClass":"InvalidTextLocationError","httpStatus":null,"severity":"error","filePath":"src/solidlsp/ls.py","lineNumber":261,"sourceCode":"\n    def get_text(self) -> str:\n        end_line = self._end_line\n        end_col = self._end_col\n        if end_line >= len(self._lines):\n            if end_line == len(self._lines) and end_col == 0:\n                # LSP convention: a range covering whole lines through EOF sometimes ends\n                # at the start of the following, non-existent line (exactly one line past\n                # the last valid index, at column 0). That is well-defined: it means\n                # \"through EOF\", so treat it as ending at the end of the actual last line.\n                end_line = len(self._lines) - 1\n                end_col = len(self._lines[end_line])\n            else:\n                # Any other out-of-range end position (further past EOF, or exactly one\n                # line past EOF but not at column 0) is not the well-defined convention\n                # above; applying the same correction there would silently assume that\n                # a column meant for a nonexistent line still applies to the corrected\n                # one, which can produce a garbage body. Reject it instead of guessing.\n                raise InvalidTextLocationError(\n                    f\"Symbol range end (line {self._end_line}, col {self._end_col}) is out of bounds \"\n                    f\"for a file with {len(self._lines)} lines\"\n                )\n\n        # extract relevant lines\n        symbol_body = \"\\n\".join(self._lines[self._start_line : end_line + 1])\n\n        # remove leading content from the first line\n        symbol_body = symbol_body[self._start_col :]\n\n        # remove trailing content from the last line\n        last_line = self._lines[end_line]\n        trailing_length = len(last_line) - end_col\n        if trailing_length > 0:\n            symbol_body = symbol_body[: -(len(last_line) - end_col)]\n\n        return symbol_body\n","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/solidlsp/ls.py#L243-L279","documentation":"SolidLSP stores symbol ranges (start/end line/col) reported by the language server. When extracting a symbol's body text via get_text, if the symbol's end position points beyond the file's actual line count, the library refuses to guess a correction and raises InvalidTextLocationError rather than return a wrong or garbage body.","triggerScenarios":"Calling get_text (directly or via body/render_html or document-symbol tests) on a Symbol whose _end_line is >= number of lines in the file and whose end column is not the accepted EOF convention (col 0 one line past EOF).","commonSituations":"Stale symbols captured before a file was shortened/edited; language servers reporting 0-based vs 1-based or off-by-one ranges; using a symbol snapshot against a different file version; LSP bugs in range reporting.","solutions":["Re-request document symbols so the Symbol's range matches the current file contents","Reload/re-read the file and confirm line count vs the symbol's reported end line","Clamp or validate symbol ranges before calling get_text in your own code","If caused by a specific language server, check for LSP version-specific range bugs and update the server"],"exampleFix":"// before\nbody = symbol.get_text()\n// after\nif symbol._end_line < len(file_lines):\n    body = symbol.get_text()\nelse:\n    symbols = ls.request_document_symbols(symbol.input_path)  # refresh stale range","handlingStrategy":"validation","validationCode":"lines = open(symbol.input_path).read().splitlines()\nif symbol_range_end_line >= len(lines):\n    # symbol range is stale; re-request symbols before calling get_text\n    symbols = ls.request_document_symbols(symbol.input_path)","typeGuard":"def has_valid_range(symbol, file_lines: list[str]) -> bool:\n    return symbol.end_line < len(file_lines)","tryCatchPattern":"try:\n    body = symbol.get_text()\nexcept InvalidTextLocationError:\n    symbols = ls.request_document_symbols(symbol.input_path)  # refresh stale range\n    body = refreshed_symbol.get_text()","preventionTips":["Always re-request symbols after editing a file","Avoid reusing Symbol objects across file revisions","Sanity-check end_line against file length before extracting bodies"],"tags":["lsp","symbol-range","out-of-bounds","stale-data"],"backgroundTag":"symbol-range-out-of-bounds","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}