{"record":{"id":"b8b4f2e0efc2221d","repo":"antlr/antlr4","slug":"the-interval-extends-past-the-end-of-the-stream","errorCode":null,"errorMessage":"the interval extends past the end of the stream","messagePattern":"the interval extends past the end of the stream","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"runtime/Java/src/org/antlr/v4/runtime/UnbufferedCharStream.java","lineNumber":331,"sourceCode":"    @Override\n    public String getSourceName() {\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn UNKNOWN_SOURCE_NAME;\n\t\t}\n\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String getText(Interval interval) {\n\t\tif (interval.a < 0 || interval.b < interval.a - 1) {\n\t\t\tthrow new IllegalArgumentException(\"invalid interval\");\n\t\t}\n\n\t\tint bufferStartIndex = getBufferStartIndex();\n\t\tif (n > 0 && data[n - 1] == Character.MAX_VALUE) {\n\t\t\tif (interval.a + interval.length() > bufferStartIndex + n) {\n\t\t\t\tthrow new IllegalArgumentException(\"the interval extends past the end of the stream\");\n\t\t\t}\n\t\t}\n\n\t\tif (interval.a < bufferStartIndex || interval.b >= bufferStartIndex + n) {\n\t\t\tthrow new UnsupportedOperationException(\"interval \"+interval+\" outside buffer: \"+\n\t\t\t                    bufferStartIndex+\"..\"+(bufferStartIndex+n-1));\n\t\t}\n\t\t// convert from absolute to local index\n\t\tint i = interval.a - bufferStartIndex;\n\t\treturn new String(data, i, interval.length());\n\t}\n\n\tprotected final int getBufferStartIndex() {\n\t\treturn currentCharIndex - p;\n\t}\n}\n","sourceCodeStart":313,"sourceCodeEnd":348,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/Java/src/org/antlr/v4/runtime/UnbufferedCharStream.java#L313-L348","documentation":"In UnbufferedCharStream.getText(Interval), once the EOF marker has been buffered (data[n-1] == Character.MAX_VALUE), the stream knows the true end of input. If the requested interval's end (a + length) extends beyond that known end, it throws IllegalArgumentException 'the interval extends past the end of the stream' — there is no text beyond EOF to return.","triggerScenarios":"getText(Interval.of(a, b)) with b beyond the last character, e.g., a stop index computed as startIndex + expectedLength that overshoots; using a token's stop index from a different, longer input.","commonSituations":"Fixed-width extraction logic (read N chars starting at X) applied to short inputs; interval arithmetic reused across streams of different lengths; highlighting code that pads ranges to line ends.","solutions":["Clamp b to the last character index (bufferStartIndex + n - 1) before calling when EOF is known to be buffered","Derive intervals from tokens of the same parse so they cannot exceed the input","Treat the empty string as the answer when a >= end of stream"],"exampleFix":"// before\nString t = stream.getText(Interval.of(a, a + width - 1)); // overshoots on short input\n\n// after\nint end = Math.min(a + width - 1, lastIndexOfStream); // track last index as you read\nString t = (a > end) ? \"\" : stream.getText(Interval.of(a, end));","handlingStrategy":"validation","validationCode":"int end = Math.min(b, knownLastIndex); // clamp to stream end (track it while consuming)\nString text = (a > end) ? \"\" : stream.getText(Interval.of(a, end));","typeGuard":null,"tryCatchPattern":"try { stream.getText(Interval.of(a, b)); }\ncatch (IllegalArgumentException e) { /* clamp b to a + available and retry, or return partial text */ }","preventionTips":["Clamp extraction ranges to the last known index","Derive ranges from tokens of the same input","Handle the empty/short-input case explicitly in fixed-width extraction code"],"tags":["antlr","char-stream","interval","eof","gettext"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}