{"record":{"id":"e26496f122f7e544","repo":"ssssssss-team/spider-flow","slug":"end-outside-of-string-e26496","errorCode":null,"errorMessage":"End outside of string.","messagePattern":"End outside of string\\.","errorType":"validation","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/Span.java","lineNumber":27,"sourceCode":"\t/** start index in source string, starting at 0 **/\n\tprivate int start;\n\n\t/** end index in source string, exclusive, starting at 0 **/\n\tprivate int end;\n\n\t/** Cached String instance to reduce pressure on GC **/\n\tprivate final String cachedText;\n\n\tpublic Span (String source) {\n\t\tthis(source, 0, source.length());\n\t}\n\n\tpublic Span (String source, int start, int end) {\n\t\tif (start > end) throw new IllegalArgumentException(\"Start must be <= end.\");\n\t\tif (start < 0) throw new IndexOutOfBoundsException(\"Start must be >= 0.\");\n\t\tif (start > source.length() - 1) \n\t\t\tthrow new IndexOutOfBoundsException(\"Start outside of string.\");\n\t\tif (end >source.length()) throw new IndexOutOfBoundsException(\"End outside of string.\");\n\n\t\tthis.source = source;\n\t\tthis.start = start;\n\t\tthis.end = end;\n\t\tthis.cachedText = source.substring(start, end);\n\t}\n\n\tpublic Span (Span start, Span end) {\n\t\tif (!start.source.equals(end.source)) throw new IllegalArgumentException(\"The two spans do not reference the same source.\");\n\t\tif (start.start > end.end) throw new IllegalArgumentException(\"Start must be <= end.\");\n\t\tif (start.start < 0) throw new IndexOutOfBoundsException(\"Start must be >= 0.\");\n\t\tif (start.start > start.source.length() - 1) throw new IndexOutOfBoundsException(\"Start outside of string.\");\n\t\tif (end.end > start.source.length()) throw new IndexOutOfBoundsException(\"End outside of string.\");\n\n\t\tthis.source = start.source;\n\t\tthis.start = start.start;\n\t\tthis.end = end.end;\n\t\tthis.cachedText = source.substring(this.start, this.end);","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/ssssssss-team/spider-flow/blob/c799cca99c7d064673dc79e6ef06a08bbc0e292d/spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/Span.java#L9-L45","documentation":"The Span(String,int,int) constructor throws IndexOutOfBoundsException(\"End outside of string.\") when end exceeds source.length(). A span cannot extend beyond the last character of the source it references. This guards the subsequent source.substring(start, end) call from failing.","triggerScenarios":"Calling new Span(source, start, end) with end > source.length(), typically after computing an end offset with an inclusive/exclusive mix-up or over-advancing a cursor.","commonSituations":"Using end = start + tokenLength where the token runs past the input; passing matched-region offsets from a regex that already account for a terminator incorrectly.","solutions":["Clamp end to source.length(): end = Math.min(end, source.length()).","Fix the offset arithmetic that produced an out-of-range end.","Verify the source string is the same (complete) text the offsets were computed against.","Use the single-argument Span(source) constructor for full-source spans."],"exampleFix":"// before\nSpan span = new Span(source, start, start + tokenLen);\n// after\nSpan span = new Span(source, start, Math.min(start + tokenLen, source.length()));","handlingStrategy":"validation","validationCode":"if (end > source.length()) throw new IllegalArgumentException(\"end \" + end + \" exceeds source length \" + source.length());\nSpan span = new Span(source, start, Math.min(end, source.length()));","typeGuard":"boolean isValidSpanRange(String source, int start, int end) {\n    return source != null && start >= 0 && start <= end && end <= source.length() && start < source.length();\n}","tryCatchPattern":"try {\n    Span span = new Span(source, start, end);\n} catch (IndexOutOfBoundsException e) {\n    Span span = new Span(source, start, source.length());\n}","preventionTips":["Treat end as exclusive and always <= source.length().","Clamp computed ends: Math.min(end, source.length()).","Recompute offsets against the exact same source string instance used elsewhere.","Test tokenizers against inputs ending exactly at token boundaries."],"tags":["parsing","index-out-of-bounds","span"],"backgroundTag":"index-out-of-bounds","analyzedSha":"c799cca99c7d064673dc79e6ef06a08bbc0e292d","analyzedAt":"2026-09-08T13:47:08.225Z","contentChangedAt":"2026-09-08T13:47:08.225Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}